Introduction —

The goal of this file is to pre-process the Strongyloides ratti RNAseq dataset originally published by Hunt et al 2016. Raw reads are aligned to the S. ratti reference transcription (PRJEB125.WBPS14.mRNA_transcripts, downloaded from WormBase Parasite on 17 August 2020)

Data Pre-Processing

A description of Kallisto alignment and data filtering/normalization steps can be found in Sr_RNAseq_Data_Processing.rmd.

Data Analysis

The limma package ( Ritchie et al 2015, Phipson et al 2016) is used to conduct pairwise differential gene expression analyses between life stages. Here, all unique pairwise comparisons between life stages are displayed as interactive volcano plots and DataTables. In this analysis, genes included in DataTables are corrected for multiple life-stage comparisons. An additional code chunk identifies all genes that are differentially expressed in any way across groups (i.e. an ANOVA analysis across life stages). This set of genes is passed into a clustering analysis that generates a heatmap of differential gene expression across life stages.

Load and Parse Preprocessed Data —

load (file = "../Outputs/SrRNAseq_data_preprocessed")
targets <- SrRNAseq.preprocessed.data$targets
annotations <- SrRNAseq.preprocessed.data$annotations
log2.cpm.filtered.norm <- SrRNAseq.preprocessed.data$log2.cpm.filtered.norm
myDGEList.filtered.norm <-SrRNAseq.preprocessed.data$myDGEList.filtered.norm

rm(SrRNAseq.preprocessed.data)

load(file = "../Strongyloides_RNAseq_Browser/Data/Sr_vDEGList")

Hierarchical Clustering and Principle Components Analysis —

# Introduction to this chunk -----------
# This code chunk starts with filtered and normalized abundance data in a data frame (not tidy).
# It will implement hierarchical clustering and PCA analyses on the data.
# It will plot various graphs and can save them in PDF files.
# Load packages ------
suppressPackageStartupMessages({
  library(tidyverse) # you're familiar with this fromt the past two lectures
  library(ggplot2)
  library(RColorBrewer)
  library(ggdendro)
  library(magrittr)
  library(factoextra)
  library(gridExtra)
  library(cowplot)
  library(dendextend)
})

# Identify variables of interest in study design file ----
group <- factor(targets$group)
batch <- factor(targets$block)
source <- factor(targets$source)

# Hierarchical clustering ---------------
# Remember: hierarchical clustering can only work on a data matrix, not a data frame

# Calculate distance matrix
# dist calculates distance between rows, so transpose data so that we get distance between samples.
# how similar are samples from each other
colnames(log2.cpm.filtered.norm)<-targets$group
distance <- dist(t(log2.cpm.filtered.norm), method = "maximum") #other distance methods are "euclidean", maximum", "manhattan", "canberra", "binary" or "minkowski"

# Calculate clusters to visualize differences. This is the hierarchical clustering.
# The methods here include: single (i.e. "friends-of-friends"), complete (i.e. complete linkage), and average (i.e. UPGMA). Here's a comparison of different types: https://en.wikipedia.org/wiki/UPGMA#Comparison_with_other_linkages
clusters <- hclust(distance, method = "complete") #other agglomeration methods are "ward.D", "ward.D2", "single", "complete", "average", "mcquitty", "median", or "centroid"
dend <- as.dendrogram(clusters) 

p1<-dend %>% 
  dendextend::set("branches_k_color", k = 5) %>% 
  dendextend::set("hang_leaves", c(0.1)) %>% 
  dendextend::set("labels_cex", c(0.5)) %>%
  dendextend::set("labels_colors", k = 5) %>% 
  dendextend::set("branches_lwd", c(0.7)) %>% 
  
  as.ggdend %>%
  ggplot (offset_labels = -0.5) +
  theme_dendro() +
  ylim(0, max(get_branches_heights(dend))) +
  labs(title = "Hierarchical Cluster Dendrogram ",
       subtitle = "filtered, TMM normalized",
       y = "Distance",
       x = "Life stage") +
  coord_fixed(1/2) +
  theme(axis.title.x = element_text(color = "black"),
        axis.title.y = element_text(angle = 90),
        axis.text.y = element_text(angle = 0),
        axis.line.y = element_line(color = "black"),
        axis.ticks.y = element_line(color = "black"),
        axis.ticks.length.y = unit(2, "mm"))
p1


# Principal component analysis (PCA) -------------
# this also works on a data matrix, not a data frame
pca.res <- prcomp(t(log2.cpm.filtered.norm), scale.=F, retx=T)
summary(pca.res) # Prints variance summary for all principal components.
Importance of components:
                            PC1     PC2     PC3      PC4      PC5     PC6     PC7     PC8
Standard deviation     113.3816 83.9229 76.8171 14.59647 12.62554 9.82530 8.57434 7.55633
Proportion of Variance   0.4833  0.2648  0.2219  0.00801  0.00599 0.00363 0.00276 0.00215
Cumulative Proportion    0.4833  0.7481  0.9700  0.97800  0.98400 0.98763 0.99039 0.99254
                           PC9    PC10    PC11    PC12    PC13      PC14
Standard deviation     7.23685 6.63777 6.15890 5.82959 5.48974 1.599e-13
Proportion of Variance 0.00197 0.00166 0.00143 0.00128 0.00113 0.000e+00
Cumulative Proportion  0.99451 0.99616 0.99759 0.99887 1.00000 1.000e+00
#pca.res$rotation #$rotation shows you how much each gene influenced each PC (called 'scores')
#pca.res$x # 'x' shows you how much each sample influenced each PC (called 'loadings')
#note that these have a magnitude and a direction (this is the basis for making a PCA plot)
## This generates a screeplot: a standard way to view eigenvalues for each PCA. Shows the proportion of variance accounted for by each PC. Plotting only the first 10 dimensions.
p2<-fviz_eig(pca.res,
             barcolor = brewer.pal(8,"Pastel2")[8],
             barfill = brewer.pal(8,"Pastel2")[8],
             linecolor = "black",
             main = "Scree plot: proportion of variance accounted for by each principal component",
             ggtheme = theme_bw()) 
p2


pc.var<-pca.res$sdev^2 # sdev^2 captures these eigenvalues from the PCA result
pc.per<-round(pc.var/sum(pc.var)*100, 1) # we can then use these eigenvalues to calculate the percentage variance explained by each PC

# Visualize the PCA result ------------------
#lets first plot any two PCs against each other
#We know how much each sample contributes to each PC (loadings), so let's plot
pca.res.df <- as_tibble(pca.res$x)

# Plotting PC1 and PC2
p3<-ggplot(pca.res.df) +
  aes(x=PC1, y=PC2, label=targets$group, 
      fill = targets$group,
      color = targets$group
  ) +
  geom_point(size=4, shape= 21, color = "black", alpha = 0.5) +
  #geom_label(color = "black", size = 2) +
  scale_fill_brewer(palette = "Set2") +
  scale_color_brewer(palette = "Set2", guide = FALSE) +
  #stat_ellipse() +
  xlab(paste0("PC1 (",pc.per[1],"%",")")) + 
  ylab(paste0("PC2 (",pc.per[2],"%",")")) +
  labs(title="Principal Components Analysis of S. stercoralis RNAseq Samples",
       sub = "Note: analysis is blind to life stage identity.",
       fill = "Life Stage") +
  scale_x_continuous(expand = c(.3, .3)) +
  scale_y_continuous(expand = c(.3, .3)) +
  coord_fixed() +
  theme_bw()
p3


# Create a PCA 'small multiples' chart ----
pca.res.df <- pca.res$x[,1:3] %>% 
  as_tibble() %>%
  add_column(sample = targets$sample,
             source = source,
             group = group,
             batch = factor(targets$block))

pca.pivot <- pivot_longer(pca.res.df, # dataframe to be pivoted
                          cols = PC1:PC3, # column names to be stored as a SINGLE variable
                          names_to = "PC", # name of that new variable (column)
                          values_to = "loadings") # name of new variable (column) storing all the values (data)
PC1<-subset(pca.pivot, PC == "PC1")
PC2 <-subset(pca.pivot, PC == "PC2")
#PC3 <- subset(pca.pivot, PC == "PC3")
#PC4 <- subset(pca.pivot, PC == "PC4")

p6<-ggplot(pca.pivot) +
  aes(x=sample, y=loadings) + # you could iteratively 'paint' different covariates onto this plot using the 'fill' aes
  geom_bar(stat="identity", fill = brewer.pal(8,"Pastel2")[8]) +
  scale_fill_brewer(palette = "Set2") +
  facet_wrap(~PC) +
  geom_bar(data = PC1, stat = "identity", aes(fill = batch)) +
  geom_bar(data = PC2, stat = "identity", aes(fill = batch)) +
  labs(title="PCA 'small multiples' plot",
       fill = "Life Stage Groups",
       caption=paste0("produced on ", Sys.time())) +
  scale_x_discrete(limits = targets$sample, labels = targets$group) +
  theme_bw() +
  coord_flip()
p6


# Graph all the plots generated in this script ----
# Plot all the plots on a grid
# p7<- grid.arrange(p1,p2,p3,p4, p5,p6,ncol = 4,
#             layout_matrix = cbind(c(1,1,1,2,2,2),c(3,3,4,4,5,5),c(6,6,6,6,6,6), c(6,6,6,6,6,6)))
# ggsave("Strongyloides stercoralis RNAseq Multivariate Analysis.pdf", 
#        plot = p7, 
#        device = "pdf",
#        #height = 17,
#        width = 22,
#        path = '../Outputs/'

Heatmap of Gene Expression Across Life Stages —

library(pheatmap)
library(RColorBrewer)
# Make a heatmap for all the genes using the Log2CPM values

diffGenes <- v.DEGList.filtered.norm$E %>%
  as_tibble(rownames = "geneID", .name_repair = "unique") %>%
  dplyr::select(!geneID) %>%
  as.matrix()
rownames(diffGenes) <- rownames(v.DEGList.filtered.norm$E)
colnames(diffGenes) <- as.character(v.DEGList.filtered.norm$targets$group)
clustColumns <- hclust(as.dist(1-cor(diffGenes, method="spearman")), method="complete")
clustRows <- hclust(as.dist(1-cor(t(diffGenes), 
                                  method="pearson")), 
                    method="complete") 
par(cex.main=1.2)

showticklabels <- c(TRUE,FALSE)
p<-pheatmap(diffGenes,
            color = RdBu(75),
            cluster_rows = clustRows,
            cluster_cols = clustColumns,
            show_rownames = F,
            scale = "row",
            angle_col = 45,
            main = "Log2 Counts Per Million (CPM) Expression Across Life Stages"
          
)

Differentially Expressed Genes —

# Introduction to this chunk ----
# This chunk uses a variance-stabilized DGEList of filtered and normalized abundance data.
# 
# These data/results are examples, a responsive version of this code is avaliable in a Shiny App.
# 
# Because we have access to biological and technical replicates, we can use statistical tools for differential expression analysis
# Useful reading on differential expression: https://ucdavis-bioinformatics-training.github.io/2018-June-RNA-Seq-Workshop/thursday/DE.html

# Load packages ----
suppressPackageStartupMessages({
  library(tidyverse)
  library(limma) # differential gene expression using linear modeling
  library(edgeR)
  library(gt) 
  library(DT) 
  library(plotly)
  library(ggthemes)
  library(RColorBrewer)
  source("../Strongyloides_RNAseq_Browser/Server/theme_Publication.R")
})

diffGenes.df <- v.DEGList.filtered.norm$E %>%
  as_tibble(rownames = "geneID", .name_repair = "unique")

# Set Expression threshold values for plotting and saving DEGs ----
adj.P.thresh <- 0.05
lfc.thresh <- 1 

group <- factor(v.DEGList.filtered.norm$targets$group)
design <- model.matrix(~0 + group) # no intercept/blocking for matrix, comparisons across group
colnames(design) <- levels(group)


# Fit a linear model to the data ----
fit <- lmFit(v.DEGList.filtered.norm, design = design)

# As an example, generate comparison matrix for a pairwise comparison ----
# iL3s vs FLF
# Note that the target/contrast goups will be divided by the number of life 
# stage groups e.g. PF+FLF/2 - iL3+iL3a+pfL1+ppL1+ppL3/5
comparison <- c('(iL3)-(FLF)')

targetStage<- comparison %>%
  str_split(pattern="-", simplify = T) %>%
  .[,1] %>%
  gsub("(", "", ., fixed = TRUE) %>%
  gsub(")", "", ., fixed = TRUE) %>%
  str_split(pattern = "\\+", simplify = T)

contrastStage<-comparison %>%
  str_split(pattern="-", simplify = T) %>%
  .[,2] %>%
  gsub("(", "", ., fixed = TRUE) %>%
  gsub(")", "", ., fixed = TRUE)  %>%
  str_split(pattern = "\\+", simplify = T)

comparison<- sapply(seq_along(comparison),function(x){
  tS <- as.vector(targetStage[x,]) %>%
    .[. != ""] 
  cS <- as.vector(contrastStage[x,]) %>%
    .[. != ""] 
  paste(paste0(tS, 
               collapse = "+") %>%
          paste0("(",.,")/",length(tS)),
        paste0(cS, 
               collapse = "+") %>%
          paste0("(",.,")/",length(cS)),
        sep = "-")
  
})

# Generate contrast matrix ----
contrast.matrix <- makeContrasts(contrasts = comparison,
                                 levels=design)

# extract the linear model fit -----
fits <- contrasts.fit(fit, contrast.matrix)
# empirical bayes smoothing of gene-wise standard deviations provides increased power (see: https://www.degruyter.com/doi/10.2202/1544-6115.1027)
ebFit <- eBayes(fits)

# Pull out the DEGs that pass a specific threshold for all pairwise comparisons ----
# Adjust for multiple comparisons using method = global. 
results <- decideTests(ebFit, method="global", adjust.method="BH", p.value = adj.P.thresh)

recode01<- function(x){
  case_when(x == 1 ~ "Up",
            x == -1 ~ "Down",
            x == 0 ~ "NotSig")
}
diffDesc <- results %>%
  as_tibble(rownames = "geneID") %>%
  dplyr::mutate(across(-geneID, unclass)) %>%
  dplyr::mutate(across(where(is.double), recode01))

# Function that identifies top DEGs between a specific contrast ----
calc_DEG_tbl <- function (ebFit, coef) {
  myTopHits.df <- limma::topTable(ebFit, adjust ="BH", 
                                  coef=coef, number=40000, 
                                  sort.by="logFC") %>%
    as_tibble(rownames = "geneID") %>%
    dplyr::rename(tStatistic = t, LogOdds = B, BH.adj.P.Val = adj.P.Val) %>%
    dplyr::relocate(UniProtKB, Description, InterPro, GO_term, WBgeneID, Str_geneID, Str_percent_homology, Ce_geneID, Ce_percent_homology, .after = LogOdds)
  
  myTopHits.df
}

list.myTopHits.df <- sapply(comparison, function(y){
  calc_DEG_tbl(ebFit, y)}, 
  simplify = FALSE, 
  USE.NAMES = TRUE)

list.myTopHits.df <- sapply(comparison, function(y){
  list.myTopHits.df[[y]] %>%
    dplyr::select(geneID, 
                  logFC, 
                  BH.adj.P.Val:Ce_percent_homology)},
  simplify = FALSE, 
  USE.NAMES = TRUE)

# Get log2CPM values and threshold information for genes of interest
list.myTopHits.df <- sapply(seq_along(comparison), function(y){
  tS<- targetStage[y,][targetStage[y,]!=""]
  cS<- contrastStage[y,][contrastStage[y,]!=""]
  
  concat_name <- function(x) {
    ifelse(x == "target", 
           paste(tS, collapse = "+"), 
           paste(cS, collapse = "+"))
  }
  
  groupAvgs <- diffGenes.df %>%
    dplyr::select(geneID, starts_with(paste0(tS,"-")), 
                  starts_with(paste0(cS,"-"))) %>%
    pivot_longer(cols = -geneID, names_to = c("group","sample"), values_to = "CPM",
                 names_sep = "-") %>%
    dplyr::mutate(contrastID = if_else(group %in% tS,"target", "contrast")) %>%
    group_by(geneID, contrastID) %>%
    dplyr::select(-sample) %>%
    summarize(mean = mean(CPM), .groups = "drop_last") %>%
    pivot_wider(names_from = contrastID, values_from = mean) %>%
    dplyr::relocate(contrast, .after = target) %>%
    dplyr::rename_with(concat_name, -geneID) %>%
    dplyr::rename_with(.cols =-geneID, .fn = ~ paste0("avg_(",.x,")"))
  
  diffGenes.df %>%
    dplyr::select(geneID, starts_with(paste0(tS,"-")), 
                  starts_with(paste0(cS,"-"))) %>%
    left_join(groupAvgs, by = "geneID") %>%
    left_join(list.myTopHits.df[[y]],., by = "geneID") %>%
    left_join(dplyr::select(diffDesc,geneID,comparison[y]), by = "geneID") %>%
    dplyr::rename(DEG_Desc=comparison[y]) %>%
    dplyr::relocate(DEG_Desc) %>%
    dplyr::relocate(logFC:Ce_percent_homology, .after = last_col())
  
},
simplify = FALSE)

comparison <- gsub("/[0-9]*","", comparison)
names(list.myTopHits.df) <- comparison

list.myTopHits.df <- sapply(comparison, function(y){
  list.myTopHits.df[[y]] %>%
    dplyr::mutate(DEG_Desc = case_when(DEG_Desc == "Up" ~ paste0("Up in ", str_split(y,'-',simplify = T)[1,1]),
                                       DEG_Desc == "Down" ~ paste0("Down in ", str_split(y,'-',simplify = T)[1,1]),
                                       DEG_Desc == "NotSig" ~ "NotSig")) 
},
simplify = FALSE, 
USE.NAMES = TRUE)

# PC1 Volcano Plot and Interactive Table ----
vplot1 <- ggplot(list.myTopHits.df[[1]]) +
  aes(y=-log10(BH.adj.P.Val), x=logFC, text = paste(geneID, "<br>",
                                                    "logFC:", round(logFC, digits = 2), "<br>",
                                                    "p-val:", format(BH.adj.P.Val, digits = 3, scientific = TRUE))) +
  geom_point(size=2) +
  geom_hline(yintercept = -log10(adj.P.thresh), 
             linetype="longdash", 
             colour="grey", 
             size=1) + 
  geom_vline(xintercept = lfc.thresh, 
             linetype="longdash", 
             colour="#BE684D", 
             size=1) +
  geom_vline(xintercept = -lfc.thresh, 
             linetype="longdash", 
             colour="#2C467A", 
             size=1) +
  labs(title = paste0('Pairwise Comparison: ',
                      gsub('-',
                           ' vs ',
                           comparison[1])),
       subtitle = paste0("grey line: p = ",
                         adj.P.thresh, "; colored lines: log-fold change = ", lfc.thresh),
       color = "GeneIDs") +
  theme_Publication() 
vplot1


# Interactive Tables
yy<- 1
tS<- targetStage[yy,][targetStage[yy,]!=""]
cS<- contrastStage[yy,][contrastStage[yy,]!=""]
sample.num.tS <- sapply(tS, function(x) {colSums(v.DEGList.filtered.norm$design)[[x]]}) %>% sum()
sample.num.cS <- sapply(cS, function(x) {colSums(v.DEGList.filtered.norm$design)[[x]]}) %>% sum()


n_num_cols <- sample.num.tS + sample.num.cS + 5


LS.datatable <- list.myTopHits.df[[yy]] %>%
  DT::datatable(rownames = FALSE,
                caption = htmltools::tags$caption(
                  style = 'caption-side: top; text-align: left; color: black',
                  htmltools::tags$b('Differentially Expressed Genes in', 
                                    htmltools::tags$em('S. ratti'), 
                                    gsub('-',' vs ',comparison[yy])),
                  htmltools::tags$br(),
                  "Threshold: p < ",
                  adj.P.thresh, "; log-fold change > ",
                  lfc.thresh,
                  htmltools::tags$br(),
                  'Values = log2 counts per million'),
                options = list(autoWidth = TRUE,
                               scrollX = TRUE,
                               scrollY = '300px',
                               scrollCollapse = TRUE,
                               order = list(n_num_cols-1, 
                                            'desc'),
                               searchHighlight = TRUE, 
                               pageLength = 25, 
                               lengthMenu = c("5",
                                              "10",
                                              "25",
                                              "50",
                                              "100"),
                               columnDefs = list(
                                 list(
                                   targets = ((n_num_cols + 
                                                 1)),
                                   render = JS(
                                     "function(data, row) {",
                                     "data.toExponential(1);",
                                     "}")
                                 ),
                                 list(
                                   targets = ((n_num_cols + 
                                                 4):(n_num_cols + 
                                                       5)),
                                   render = JS(
                                     "function(data, type, row, meta) {",
                                     "return type === 'display' && data.length > 20 ?",
                                     "'<span title=\"' + data + '\">' + data.substr(0, 20) + '...</span>' : data;",
                                     "}")
                                 ),
                                 list(targets = "_all",
                                      class="dt-right")
                               ),
                               rowCallback = JS(c(
                                 "function(row, data){",
                                 "  for(var i=0; i<data.length; i++){",
                                 "    if(data[i] === null){",
                                 "      $('td:eq('+i+')', row).html('NA')",
                                 "        .css({'color': 'rgb(151,151,151)', 'font-style': 'italic'});",
                                 "    }",
                                 "  }",
                                 "}"  
                               ))
                               
                )) 
LS.datatable <- LS.datatable %>%
  DT::formatRound(columns=c(3:n_num_cols), 
                  digits=3)

LS.datatable <- LS.datatable %>%
  DT::formatRound(columns=c(n_num_cols+2, 
                            n_num_cols+9,
                            n_num_cols+11), 
                  digits=2)

LS.datatable <- LS.datatable %>%
  DT::formatSignif(columns=c(n_num_cols+1), 
                   digits=3)

LS.datatable

NA

Benchmarking


suppressPackageStartupMessages({
  library(openxlsx)
  library(tidyverse)
  library(ggplot2)
})
# Load Hunt Dataset: iL3 vs FLF comparison
temp.dat <-  read.xlsx ("../Benchmarking/ng.3495-S5.xlsx", 
                        sheet = 2, startRow = 2)

Hunt.dat <- tibble(geneID = temp.dat$X2, logFC = temp.dat$logFC)
rm(temp.dat)

# Rename Results of iL3 vs FLF comparison from Browser
Browser.dat <- list.myTopHits.df$`(iL3)-(FLF)` %>%
  dplyr::select(geneID, logFC)

print(paste('Total number of genes in Hunt *et al* 2016 iL3 vs FLF comparison tab:',nrow(Hunt.dat)))
[1] "Total number of genes in Hunt *et al* 2016 iL3 vs FLF comparison tab: 11146"
print(paste('Total number of genes in Str-RNAseq Browser iL3 vs FLF output file:', nrow(Browser.dat))) 
[1] "Total number of genes in Str-RNAseq Browser iL3 vs FLF output file: 12035"
# The plot below takes the genes with LogFC results in both the Browser and Hunt databases, and plots the two sets against each other. 
plotting.all <- inner_join(Browser.dat, Hunt.dat, by = "geneID")
ggplot(plotting.all, aes(x = logFC.x, y = logFC.y)) +
  geom_smooth(method=lm, color = 'red', formula = "y ~ x") +
  geom_point(shape=16, size=3, alpha = 0.8) +
  labs(title = "All Overlapping Genes: Str-Browser vs Hunt Data",
       subtitle = "iL3 vs FLF",
       caption = "points = genes; red line = linear model (y ~ x)",
       x = "Str-Browser LogFC",
       y = "Hunt LogFC") +
  coord_equal() +
  theme_bw()

NA
NA

Cluster DEGs into functional modules —

Functional Enrichment Analysis —

nrow(GO.present.Compara.absent)
[1] 543
LS0tCnRpdGxlOiBPZmZsaW5lIERhdGEgQW5hbHlzaXMgb2YgU3Ryb25neWxvaWRlcyByYXR0aSBidWxrIFJOQXNlcSBkYXRhCm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgZGZfcHJpbnQ6IHBhZ2VkCiAgICB0b2M6IHllcwogIGh0bWxfbm90ZWJvb2s6CiAgICB0b2M6IHllcwotLS0KCiMgSW50cm9kdWN0aW9uIC0tLQpUaGUgZ29hbCBvZiB0aGlzIGZpbGUgaXMgdG8gcHJlLXByb2Nlc3MgdGhlICpTdHJvbmd5bG9pZGVzIHJhdHRpKiBSTkFzZXEgZGF0YXNldCBvcmlnaW5hbGx5IHB1Ymxpc2hlZCBieSBbSHVudCAqZXQgYWwqIDIwMTZdKGh0dHBzOi8vd3d3Lm5hdHVyZS5jb20vYXJ0aWNsZXMvbmcuMzQ5NSkuIFJhdyByZWFkcyBhcmUgYWxpZ25lZCB0byB0aGUgKlMuIHJhdHRpKiByZWZlcmVuY2UgdHJhbnNjcmlwdGlvbiAoUFJKRUIxMjUuV0JQUzE0Lm1STkFfdHJhbnNjcmlwdHMsIGRvd25sb2FkZWQgZnJvbSBbV29ybUJhc2UgUGFyYXNpdGVdKGh0dHBzOi8vcGFyYXNpdGUud29ybWJhc2Uub3JnL1N0cm9uZ3lsb2lkZXNfcmF0dGlfcHJqZWIxMjUvSW5mby9JbmRleCkgb24gMTcgQXVndXN0IDIwMjApCgojIyMgRGF0YSBQcmUtUHJvY2Vzc2luZyAgICAKQSBkZXNjcmlwdGlvbiBvZiBLYWxsaXN0byBhbGlnbm1lbnQgYW5kIGRhdGEgZmlsdGVyaW5nL25vcm1hbGl6YXRpb24gc3RlcHMgY2FuIGJlIGZvdW5kIGluIGBTcl9STkFzZXFfRGF0YV9Qcm9jZXNzaW5nLnJtZGAuIAoKIyMjIERhdGEgQW5hbHlzaXMgICAKVGhlIGxpbW1hIHBhY2thZ2UgKCBbUml0Y2hpZSAqZXQgYWwqIDIwMTVdKGh0dHBzOi8vcHVibWVkLm5jYmkubmxtLm5paC5nb3YvMjU2MDU3OTIvKSwgW1BoaXBzb24gKmV0IGFsKiAyMDE2XShodHRwczovL3d3dy5uY2JpLm5sbS5uaWguZ292L3BtYy9hcnRpY2xlcy9QTUM1MzczODEyLykpIGlzIHVzZWQgdG8gY29uZHVjdCBwYWlyd2lzZSBkaWZmZXJlbnRpYWwgZ2VuZSBleHByZXNzaW9uIGFuYWx5c2VzIGJldHdlZW4gbGlmZSBzdGFnZXMuIEhlcmUsIGFsbCB1bmlxdWUgcGFpcndpc2UgY29tcGFyaXNvbnMgYmV0d2VlbiBsaWZlIHN0YWdlcyBhcmUgZGlzcGxheWVkIGFzIGludGVyYWN0aXZlIHZvbGNhbm8gcGxvdHMgYW5kIERhdGFUYWJsZXMuIEluIHRoaXMgYW5hbHlzaXMsIGdlbmVzIGluY2x1ZGVkIGluIERhdGFUYWJsZXMgYXJlIGNvcnJlY3RlZCBmb3IgbXVsdGlwbGUgbGlmZS1zdGFnZSBjb21wYXJpc29ucy4gQW4gYWRkaXRpb25hbCBjb2RlIGNodW5rIGlkZW50aWZpZXMgYWxsIGdlbmVzIHRoYXQgYXJlIGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZCBpbiBhbnkgd2F5IGFjcm9zcyBncm91cHMgKGkuZS4gYW4gQU5PVkEgYW5hbHlzaXMgYWNyb3NzIGxpZmUgc3RhZ2VzKS4gVGhpcyBzZXQgb2YgZ2VuZXMgaXMgcGFzc2VkIGludG8gYSBjbHVzdGVyaW5nIGFuYWx5c2lzIHRoYXQgZ2VuZXJhdGVzIGEgaGVhdG1hcCBvZiBkaWZmZXJlbnRpYWwgZ2VuZSBleHByZXNzaW9uIGFjcm9zcyBsaWZlIHN0YWdlcy4KCgojIExvYWQgYW5kIFBhcnNlIFByZXByb2Nlc3NlZCBEYXRhIC0tLQpgYGB7ciBsb2FkQmFzZURhdGF9CmxvYWQgKGZpbGUgPSAiLi4vT3V0cHV0cy9TclJOQXNlcV9kYXRhX3ByZXByb2Nlc3NlZCIpCnRhcmdldHMgPC0gU3JSTkFzZXEucHJlcHJvY2Vzc2VkLmRhdGEkdGFyZ2V0cwphbm5vdGF0aW9ucyA8LSBTclJOQXNlcS5wcmVwcm9jZXNzZWQuZGF0YSRhbm5vdGF0aW9ucwpsb2cyLmNwbS5maWx0ZXJlZC5ub3JtIDwtIFNyUk5Bc2VxLnByZXByb2Nlc3NlZC5kYXRhJGxvZzIuY3BtLmZpbHRlcmVkLm5vcm0KbXlER0VMaXN0LmZpbHRlcmVkLm5vcm0gPC1TclJOQXNlcS5wcmVwcm9jZXNzZWQuZGF0YSRteURHRUxpc3QuZmlsdGVyZWQubm9ybQoKcm0oU3JSTkFzZXEucHJlcHJvY2Vzc2VkLmRhdGEpCgpsb2FkKGZpbGUgPSAiLi4vU3Ryb25neWxvaWRlc19STkFzZXFfQnJvd3Nlci9EYXRhL1NyX3ZERUdMaXN0IikKCmBgYAoKCiMgSGllcmFyY2hpY2FsIENsdXN0ZXJpbmcgYW5kIFByaW5jaXBsZSBDb21wb25lbnRzIEFuYWx5c2lzIC0tLQpgYGB7ciBtdWx0aXZhcmlhdGV9CiMgSW50cm9kdWN0aW9uIHRvIHRoaXMgY2h1bmsgLS0tLS0tLS0tLS0KIyBUaGlzIGNvZGUgY2h1bmsgc3RhcnRzIHdpdGggZmlsdGVyZWQgYW5kIG5vcm1hbGl6ZWQgYWJ1bmRhbmNlIGRhdGEgaW4gYSBkYXRhIGZyYW1lIChub3QgdGlkeSkuCiMgSXQgd2lsbCBpbXBsZW1lbnQgaGllcmFyY2hpY2FsIGNsdXN0ZXJpbmcgYW5kIFBDQSBhbmFseXNlcyBvbiB0aGUgZGF0YS4KIyBJdCB3aWxsIHBsb3QgdmFyaW91cyBncmFwaHMgYW5kIGNhbiBzYXZlIHRoZW0gaW4gUERGIGZpbGVzLgojIExvYWQgcGFja2FnZXMgLS0tLS0tCnN1cHByZXNzUGFja2FnZVN0YXJ0dXBNZXNzYWdlcyh7CiAgbGlicmFyeSh0aWR5dmVyc2UpICMgeW91J3JlIGZhbWlsaWFyIHdpdGggdGhpcyBmcm9tdCB0aGUgcGFzdCB0d28gbGVjdHVyZXMKICBsaWJyYXJ5KGdncGxvdDIpCiAgbGlicmFyeShSQ29sb3JCcmV3ZXIpCiAgbGlicmFyeShnZ2RlbmRybykKICBsaWJyYXJ5KG1hZ3JpdHRyKQogIGxpYnJhcnkoZmFjdG9leHRyYSkKICBsaWJyYXJ5KGdyaWRFeHRyYSkKICBsaWJyYXJ5KGNvd3Bsb3QpCiAgbGlicmFyeShkZW5kZXh0ZW5kKQp9KQoKIyBJZGVudGlmeSB2YXJpYWJsZXMgb2YgaW50ZXJlc3QgaW4gc3R1ZHkgZGVzaWduIGZpbGUgLS0tLQpncm91cCA8LSBmYWN0b3IodGFyZ2V0cyRncm91cCkKYmF0Y2ggPC0gZmFjdG9yKHRhcmdldHMkYmxvY2spCnNvdXJjZSA8LSBmYWN0b3IodGFyZ2V0cyRzb3VyY2UpCgojIEhpZXJhcmNoaWNhbCBjbHVzdGVyaW5nIC0tLS0tLS0tLS0tLS0tLQojIFJlbWVtYmVyOiBoaWVyYXJjaGljYWwgY2x1c3RlcmluZyBjYW4gb25seSB3b3JrIG9uIGEgZGF0YSBtYXRyaXgsIG5vdCBhIGRhdGEgZnJhbWUKCiMgQ2FsY3VsYXRlIGRpc3RhbmNlIG1hdHJpeAojIGRpc3QgY2FsY3VsYXRlcyBkaXN0YW5jZSBiZXR3ZWVuIHJvd3MsIHNvIHRyYW5zcG9zZSBkYXRhIHNvIHRoYXQgd2UgZ2V0IGRpc3RhbmNlIGJldHdlZW4gc2FtcGxlcy4KIyBob3cgc2ltaWxhciBhcmUgc2FtcGxlcyBmcm9tIGVhY2ggb3RoZXIKY29sbmFtZXMobG9nMi5jcG0uZmlsdGVyZWQubm9ybSk8LXRhcmdldHMkZ3JvdXAKZGlzdGFuY2UgPC0gZGlzdCh0KGxvZzIuY3BtLmZpbHRlcmVkLm5vcm0pLCBtZXRob2QgPSAibWF4aW11bSIpICNvdGhlciBkaXN0YW5jZSBtZXRob2RzIGFyZSAiZXVjbGlkZWFuIiwgbWF4aW11bSIsICJtYW5oYXR0YW4iLCAiY2FuYmVycmEiLCAiYmluYXJ5IiBvciAibWlua293c2tpIgoKIyBDYWxjdWxhdGUgY2x1c3RlcnMgdG8gdmlzdWFsaXplIGRpZmZlcmVuY2VzLiBUaGlzIGlzIHRoZSBoaWVyYXJjaGljYWwgY2x1c3RlcmluZy4KIyBUaGUgbWV0aG9kcyBoZXJlIGluY2x1ZGU6IHNpbmdsZSAoaS5lLiAiZnJpZW5kcy1vZi1mcmllbmRzIiksIGNvbXBsZXRlIChpLmUuIGNvbXBsZXRlIGxpbmthZ2UpLCBhbmQgYXZlcmFnZSAoaS5lLiBVUEdNQSkuIEhlcmUncyBhIGNvbXBhcmlzb24gb2YgZGlmZmVyZW50IHR5cGVzOiBodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9VUEdNQSNDb21wYXJpc29uX3dpdGhfb3RoZXJfbGlua2FnZXMKY2x1c3RlcnMgPC0gaGNsdXN0KGRpc3RhbmNlLCBtZXRob2QgPSAiY29tcGxldGUiKSAjb3RoZXIgYWdnbG9tZXJhdGlvbiBtZXRob2RzIGFyZSAid2FyZC5EIiwgIndhcmQuRDIiLCAic2luZ2xlIiwgImNvbXBsZXRlIiwgImF2ZXJhZ2UiLCAibWNxdWl0dHkiLCAibWVkaWFuIiwgb3IgImNlbnRyb2lkIgpkZW5kIDwtIGFzLmRlbmRyb2dyYW0oY2x1c3RlcnMpIAoKcDE8LWRlbmQgJT4lIAogIGRlbmRleHRlbmQ6OnNldCgiYnJhbmNoZXNfa19jb2xvciIsIGsgPSA1KSAlPiUgCiAgZGVuZGV4dGVuZDo6c2V0KCJoYW5nX2xlYXZlcyIsIGMoMC4xKSkgJT4lIAogIGRlbmRleHRlbmQ6OnNldCgibGFiZWxzX2NleCIsIGMoMC41KSkgJT4lCiAgZGVuZGV4dGVuZDo6c2V0KCJsYWJlbHNfY29sb3JzIiwgayA9IDUpICU+JSAKICBkZW5kZXh0ZW5kOjpzZXQoImJyYW5jaGVzX2x3ZCIsIGMoMC43KSkgJT4lIAogIAogIGFzLmdnZGVuZCAlPiUKICBnZ3Bsb3QgKG9mZnNldF9sYWJlbHMgPSAtMC41KSArCiAgdGhlbWVfZGVuZHJvKCkgKwogIHlsaW0oMCwgbWF4KGdldF9icmFuY2hlc19oZWlnaHRzKGRlbmQpKSkgKwogIGxhYnModGl0bGUgPSAiSGllcmFyY2hpY2FsIENsdXN0ZXIgRGVuZHJvZ3JhbSAiLAogICAgICAgc3VidGl0bGUgPSAiZmlsdGVyZWQsIFRNTSBub3JtYWxpemVkIiwKICAgICAgIHkgPSAiRGlzdGFuY2UiLAogICAgICAgeCA9ICJMaWZlIHN0YWdlIikgKwogIGNvb3JkX2ZpeGVkKDEvMikgKwogIHRoZW1lKGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChjb2xvciA9ICJibGFjayIpLAogICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwKSwKICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDApLAogICAgICAgIGF4aXMubGluZS55ID0gZWxlbWVudF9saW5lKGNvbG9yID0gImJsYWNrIiksCiAgICAgICAgYXhpcy50aWNrcy55ID0gZWxlbWVudF9saW5lKGNvbG9yID0gImJsYWNrIiksCiAgICAgICAgYXhpcy50aWNrcy5sZW5ndGgueSA9IHVuaXQoMiwgIm1tIikpCnAxCgojIFByaW5jaXBhbCBjb21wb25lbnQgYW5hbHlzaXMgKFBDQSkgLS0tLS0tLS0tLS0tLQojIHRoaXMgYWxzbyB3b3JrcyBvbiBhIGRhdGEgbWF0cml4LCBub3QgYSBkYXRhIGZyYW1lCnBjYS5yZXMgPC0gcHJjb21wKHQobG9nMi5jcG0uZmlsdGVyZWQubm9ybSksIHNjYWxlLj1GLCByZXR4PVQpCnN1bW1hcnkocGNhLnJlcykgIyBQcmludHMgdmFyaWFuY2Ugc3VtbWFyeSBmb3IgYWxsIHByaW5jaXBhbCBjb21wb25lbnRzLgoKI3BjYS5yZXMkcm90YXRpb24gIyRyb3RhdGlvbiBzaG93cyB5b3UgaG93IG11Y2ggZWFjaCBnZW5lIGluZmx1ZW5jZWQgZWFjaCBQQyAoY2FsbGVkICdzY29yZXMnKQojcGNhLnJlcyR4ICMgJ3gnIHNob3dzIHlvdSBob3cgbXVjaCBlYWNoIHNhbXBsZSBpbmZsdWVuY2VkIGVhY2ggUEMgKGNhbGxlZCAnbG9hZGluZ3MnKQojbm90ZSB0aGF0IHRoZXNlIGhhdmUgYSBtYWduaXR1ZGUgYW5kIGEgZGlyZWN0aW9uICh0aGlzIGlzIHRoZSBiYXNpcyBmb3IgbWFraW5nIGEgUENBIHBsb3QpCiMjIFRoaXMgZ2VuZXJhdGVzIGEgc2NyZWVwbG90OiBhIHN0YW5kYXJkIHdheSB0byB2aWV3IGVpZ2VudmFsdWVzIGZvciBlYWNoIFBDQS4gU2hvd3MgdGhlIHByb3BvcnRpb24gb2YgdmFyaWFuY2UgYWNjb3VudGVkIGZvciBieSBlYWNoIFBDLiBQbG90dGluZyBvbmx5IHRoZSBmaXJzdCAxMCBkaW1lbnNpb25zLgpwMjwtZnZpel9laWcocGNhLnJlcywKICAgICAgICAgICAgIGJhcmNvbG9yID0gYnJld2VyLnBhbCg4LCJQYXN0ZWwyIilbOF0sCiAgICAgICAgICAgICBiYXJmaWxsID0gYnJld2VyLnBhbCg4LCJQYXN0ZWwyIilbOF0sCiAgICAgICAgICAgICBsaW5lY29sb3IgPSAiYmxhY2siLAogICAgICAgICAgICAgbWFpbiA9ICJTY3JlZSBwbG90OiBwcm9wb3J0aW9uIG9mIHZhcmlhbmNlIGFjY291bnRlZCBmb3IgYnkgZWFjaCBwcmluY2lwYWwgY29tcG9uZW50IiwKICAgICAgICAgICAgIGdndGhlbWUgPSB0aGVtZV9idygpKSAKcDIKCnBjLnZhcjwtcGNhLnJlcyRzZGV2XjIgIyBzZGV2XjIgY2FwdHVyZXMgdGhlc2UgZWlnZW52YWx1ZXMgZnJvbSB0aGUgUENBIHJlc3VsdApwYy5wZXI8LXJvdW5kKHBjLnZhci9zdW0ocGMudmFyKSoxMDAsIDEpICMgd2UgY2FuIHRoZW4gdXNlIHRoZXNlIGVpZ2VudmFsdWVzIHRvIGNhbGN1bGF0ZSB0aGUgcGVyY2VudGFnZSB2YXJpYW5jZSBleHBsYWluZWQgYnkgZWFjaCBQQwoKIyBWaXN1YWxpemUgdGhlIFBDQSByZXN1bHQgLS0tLS0tLS0tLS0tLS0tLS0tCiNsZXRzIGZpcnN0IHBsb3QgYW55IHR3byBQQ3MgYWdhaW5zdCBlYWNoIG90aGVyCiNXZSBrbm93IGhvdyBtdWNoIGVhY2ggc2FtcGxlIGNvbnRyaWJ1dGVzIHRvIGVhY2ggUEMgKGxvYWRpbmdzKSwgc28gbGV0J3MgcGxvdApwY2EucmVzLmRmIDwtIGFzX3RpYmJsZShwY2EucmVzJHgpCgojIFBsb3R0aW5nIFBDMSBhbmQgUEMyCnAzPC1nZ3Bsb3QocGNhLnJlcy5kZikgKwogIGFlcyh4PVBDMSwgeT1QQzIsIGxhYmVsPXRhcmdldHMkZ3JvdXAsIAogICAgICBmaWxsID0gdGFyZ2V0cyRncm91cCwKICAgICAgY29sb3IgPSB0YXJnZXRzJGdyb3VwCiAgKSArCiAgZ2VvbV9wb2ludChzaXplPTQsIHNoYXBlPSAyMSwgY29sb3IgPSAiYmxhY2siLCBhbHBoYSA9IDAuNSkgKwogICNnZW9tX2xhYmVsKGNvbG9yID0gImJsYWNrIiwgc2l6ZSA9IDIpICsKICBzY2FsZV9maWxsX2JyZXdlcihwYWxldHRlID0gIlNldDIiKSArCiAgc2NhbGVfY29sb3JfYnJld2VyKHBhbGV0dGUgPSAiU2V0MiIsIGd1aWRlID0gRkFMU0UpICsKICAjc3RhdF9lbGxpcHNlKCkgKwogIHhsYWIocGFzdGUwKCJQQzEgKCIscGMucGVyWzFdLCIlIiwiKSIpKSArIAogIHlsYWIocGFzdGUwKCJQQzIgKCIscGMucGVyWzJdLCIlIiwiKSIpKSArCiAgbGFicyh0aXRsZT0iUHJpbmNpcGFsIENvbXBvbmVudHMgQW5hbHlzaXMgb2YgUy4gc3RlcmNvcmFsaXMgUk5Bc2VxIFNhbXBsZXMiLAogICAgICAgc3ViID0gIk5vdGU6IGFuYWx5c2lzIGlzIGJsaW5kIHRvIGxpZmUgc3RhZ2UgaWRlbnRpdHkuIiwKICAgICAgIGZpbGwgPSAiTGlmZSBTdGFnZSIpICsKICBzY2FsZV94X2NvbnRpbnVvdXMoZXhwYW5kID0gYyguMywgLjMpKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGV4cGFuZCA9IGMoLjMsIC4zKSkgKwogIGNvb3JkX2ZpeGVkKCkgKwogIHRoZW1lX2J3KCkKcDMKCiMgQ3JlYXRlIGEgUENBICdzbWFsbCBtdWx0aXBsZXMnIGNoYXJ0IC0tLS0KcGNhLnJlcy5kZiA8LSBwY2EucmVzJHhbLDE6M10gJT4lIAogIGFzX3RpYmJsZSgpICU+JQogIGFkZF9jb2x1bW4oc2FtcGxlID0gdGFyZ2V0cyRzYW1wbGUsCiAgICAgICAgICAgICBzb3VyY2UgPSBzb3VyY2UsCiAgICAgICAgICAgICBncm91cCA9IGdyb3VwLAogICAgICAgICAgICAgYmF0Y2ggPSBmYWN0b3IodGFyZ2V0cyRibG9jaykpCgpwY2EucGl2b3QgPC0gcGl2b3RfbG9uZ2VyKHBjYS5yZXMuZGYsICMgZGF0YWZyYW1lIHRvIGJlIHBpdm90ZWQKICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xzID0gUEMxOlBDMywgIyBjb2x1bW4gbmFtZXMgdG8gYmUgc3RvcmVkIGFzIGEgU0lOR0xFIHZhcmlhYmxlCiAgICAgICAgICAgICAgICAgICAgICAgICAgbmFtZXNfdG8gPSAiUEMiLCAjIG5hbWUgb2YgdGhhdCBuZXcgdmFyaWFibGUgKGNvbHVtbikKICAgICAgICAgICAgICAgICAgICAgICAgICB2YWx1ZXNfdG8gPSAibG9hZGluZ3MiKSAjIG5hbWUgb2YgbmV3IHZhcmlhYmxlIChjb2x1bW4pIHN0b3JpbmcgYWxsIHRoZSB2YWx1ZXMgKGRhdGEpClBDMTwtc3Vic2V0KHBjYS5waXZvdCwgUEMgPT0gIlBDMSIpClBDMiA8LXN1YnNldChwY2EucGl2b3QsIFBDID09ICJQQzIiKQojUEMzIDwtIHN1YnNldChwY2EucGl2b3QsIFBDID09ICJQQzMiKQojUEM0IDwtIHN1YnNldChwY2EucGl2b3QsIFBDID09ICJQQzQiKQoKcDY8LWdncGxvdChwY2EucGl2b3QpICsKICBhZXMoeD1zYW1wbGUsIHk9bG9hZGluZ3MpICsgIyB5b3UgY291bGQgaXRlcmF0aXZlbHkgJ3BhaW50JyBkaWZmZXJlbnQgY292YXJpYXRlcyBvbnRvIHRoaXMgcGxvdCB1c2luZyB0aGUgJ2ZpbGwnIGFlcwogIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5IiwgZmlsbCA9IGJyZXdlci5wYWwoOCwiUGFzdGVsMiIpWzhdKSArCiAgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZSA9ICJTZXQyIikgKwogIGZhY2V0X3dyYXAoflBDKSArCiAgZ2VvbV9iYXIoZGF0YSA9IFBDMSwgc3RhdCA9ICJpZGVudGl0eSIsIGFlcyhmaWxsID0gYmF0Y2gpKSArCiAgZ2VvbV9iYXIoZGF0YSA9IFBDMiwgc3RhdCA9ICJpZGVudGl0eSIsIGFlcyhmaWxsID0gYmF0Y2gpKSArCiAgbGFicyh0aXRsZT0iUENBICdzbWFsbCBtdWx0aXBsZXMnIHBsb3QiLAogICAgICAgZmlsbCA9ICJMaWZlIFN0YWdlIEdyb3VwcyIsCiAgICAgICBjYXB0aW9uPXBhc3RlMCgicHJvZHVjZWQgb24gIiwgU3lzLnRpbWUoKSkpICsKICBzY2FsZV94X2Rpc2NyZXRlKGxpbWl0cyA9IHRhcmdldHMkc2FtcGxlLCBsYWJlbHMgPSB0YXJnZXRzJGdyb3VwKSArCiAgdGhlbWVfYncoKSArCiAgY29vcmRfZmxpcCgpCnA2CgojIEdyYXBoIGFsbCB0aGUgcGxvdHMgZ2VuZXJhdGVkIGluIHRoaXMgc2NyaXB0IC0tLS0KIyBQbG90IGFsbCB0aGUgcGxvdHMgb24gYSBncmlkCiMgcDc8LSBncmlkLmFycmFuZ2UocDEscDIscDMscDQsIHA1LHA2LG5jb2wgPSA0LAojICAgICAgICAgICAgIGxheW91dF9tYXRyaXggPSBjYmluZChjKDEsMSwxLDIsMiwyKSxjKDMsMyw0LDQsNSw1KSxjKDYsNiw2LDYsNiw2KSwgYyg2LDYsNiw2LDYsNikpKQojIGdnc2F2ZSgiU3Ryb25neWxvaWRlcyBzdGVyY29yYWxpcyBSTkFzZXEgTXVsdGl2YXJpYXRlIEFuYWx5c2lzLnBkZiIsIAojICAgICAgICBwbG90ID0gcDcsIAojICAgICAgICBkZXZpY2UgPSAicGRmIiwKIyAgICAgICAgI2hlaWdodCA9IDE3LAojICAgICAgICB3aWR0aCA9IDIyLAojICAgICAgICBwYXRoID0gJy4uL091dHB1dHMvJwoKYGBgCiMgSGVhdG1hcCBvZiBHZW5lIEV4cHJlc3Npb24gQWNyb3NzIExpZmUgU3RhZ2VzIC0tLQpgYGB7ciBnZW5lLmV4cHJlc3Npb24uaGVhdG1hcH0KbGlicmFyeShwaGVhdG1hcCkKbGlicmFyeShSQ29sb3JCcmV3ZXIpCiMgTWFrZSBhIGhlYXRtYXAgZm9yIGFsbCB0aGUgZ2VuZXMgdXNpbmcgdGhlIExvZzJDUE0gdmFsdWVzCgpkaWZmR2VuZXMgPC0gdi5ERUdMaXN0LmZpbHRlcmVkLm5vcm0kRSAlPiUKICBhc190aWJibGUocm93bmFtZXMgPSAiZ2VuZUlEIiwgLm5hbWVfcmVwYWlyID0gInVuaXF1ZSIpICU+JQogIGRwbHlyOjpzZWxlY3QoIWdlbmVJRCkgJT4lCiAgYXMubWF0cml4KCkKcm93bmFtZXMoZGlmZkdlbmVzKSA8LSByb3duYW1lcyh2LkRFR0xpc3QuZmlsdGVyZWQubm9ybSRFKQpjb2xuYW1lcyhkaWZmR2VuZXMpIDwtIGFzLmNoYXJhY3Rlcih2LkRFR0xpc3QuZmlsdGVyZWQubm9ybSR0YXJnZXRzJGdyb3VwKQpjbHVzdENvbHVtbnMgPC0gaGNsdXN0KGFzLmRpc3QoMS1jb3IoZGlmZkdlbmVzLCBtZXRob2Q9InNwZWFybWFuIikpLCBtZXRob2Q9ImNvbXBsZXRlIikKY2x1c3RSb3dzIDwtIGhjbHVzdChhcy5kaXN0KDEtY29yKHQoZGlmZkdlbmVzKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtZXRob2Q9InBlYXJzb24iKSksIAogICAgICAgICAgICAgICAgICAgIG1ldGhvZD0iY29tcGxldGUiKSAKcGFyKGNleC5tYWluPTEuMikKCnNob3d0aWNrbGFiZWxzIDwtIGMoVFJVRSxGQUxTRSkKcDwtcGhlYXRtYXAoZGlmZkdlbmVzLAogICAgICAgICAgICBjb2xvciA9IFJkQnUoNzUpLAogICAgICAgICAgICBjbHVzdGVyX3Jvd3MgPSBjbHVzdFJvd3MsCiAgICAgICAgICAgIGNsdXN0ZXJfY29scyA9IGNsdXN0Q29sdW1ucywKICAgICAgICAgICAgc2hvd19yb3duYW1lcyA9IEYsCiAgICAgICAgICAgIHNjYWxlID0gInJvdyIsCiAgICAgICAgICAgIGFuZ2xlX2NvbCA9IDQ1LAogICAgICAgICAgICBtYWluID0gIkxvZzIgQ291bnRzIFBlciBNaWxsaW9uIChDUE0pIEV4cHJlc3Npb24gQWNyb3NzIExpZmUgU3RhZ2VzIgogICAgICAgICAgCikKCmBgYAoKCiMgRGlmZmVyZW50aWFsbHkgRXhwcmVzc2VkIEdlbmVzIC0tLQpgYGB7ciBERUcsIGVjaG89VFJVRSwgd2FybmluZz1GQUxTRSwgcGFnZWQucHJpbnQ9VFJVRX0KIyBJbnRyb2R1Y3Rpb24gdG8gdGhpcyBjaHVuayAtLS0tCiMgVGhpcyBjaHVuayB1c2VzIGEgdmFyaWFuY2Utc3RhYmlsaXplZCBER0VMaXN0IG9mIGZpbHRlcmVkIGFuZCBub3JtYWxpemVkIGFidW5kYW5jZSBkYXRhLgojIAojIFRoZXNlIGRhdGEvcmVzdWx0cyBhcmUgZXhhbXBsZXMsIGEgcmVzcG9uc2l2ZSB2ZXJzaW9uIG9mIHRoaXMgY29kZSBpcyBhdmFsaWFibGUgaW4gYSBTaGlueSBBcHAuCiMgCiMgQmVjYXVzZSB3ZSBoYXZlIGFjY2VzcyB0byBiaW9sb2dpY2FsIGFuZCB0ZWNobmljYWwgcmVwbGljYXRlcywgd2UgY2FuIHVzZSBzdGF0aXN0aWNhbCB0b29scyBmb3IgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gYW5hbHlzaXMKIyBVc2VmdWwgcmVhZGluZyBvbiBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbjogaHR0cHM6Ly91Y2RhdmlzLWJpb2luZm9ybWF0aWNzLXRyYWluaW5nLmdpdGh1Yi5pby8yMDE4LUp1bmUtUk5BLVNlcS1Xb3Jrc2hvcC90aHVyc2RheS9ERS5odG1sCgojIExvYWQgcGFja2FnZXMgLS0tLQpzdXBwcmVzc1BhY2thZ2VTdGFydHVwTWVzc2FnZXMoewogIGxpYnJhcnkodGlkeXZlcnNlKQogIGxpYnJhcnkobGltbWEpICMgZGlmZmVyZW50aWFsIGdlbmUgZXhwcmVzc2lvbiB1c2luZyBsaW5lYXIgbW9kZWxpbmcKICBsaWJyYXJ5KGVkZ2VSKQogIGxpYnJhcnkoZ3QpIAogIGxpYnJhcnkoRFQpIAogIGxpYnJhcnkocGxvdGx5KQogIGxpYnJhcnkoZ2d0aGVtZXMpCiAgbGlicmFyeShSQ29sb3JCcmV3ZXIpCiAgc291cmNlKCIuLi9TdHJvbmd5bG9pZGVzX1JOQXNlcV9Ccm93c2VyL1NlcnZlci90aGVtZV9QdWJsaWNhdGlvbi5SIikKfSkKCmRpZmZHZW5lcy5kZiA8LSB2LkRFR0xpc3QuZmlsdGVyZWQubm9ybSRFICU+JQogIGFzX3RpYmJsZShyb3duYW1lcyA9ICJnZW5lSUQiLCAubmFtZV9yZXBhaXIgPSAidW5pcXVlIikKCiMgU2V0IEV4cHJlc3Npb24gdGhyZXNob2xkIHZhbHVlcyBmb3IgcGxvdHRpbmcgYW5kIHNhdmluZyBERUdzIC0tLS0KYWRqLlAudGhyZXNoIDwtIDAuMDUKbGZjLnRocmVzaCA8LSAxIAoKZ3JvdXAgPC0gZmFjdG9yKHYuREVHTGlzdC5maWx0ZXJlZC5ub3JtJHRhcmdldHMkZ3JvdXApCmRlc2lnbiA8LSBtb2RlbC5tYXRyaXgofjAgKyBncm91cCkgIyBubyBpbnRlcmNlcHQvYmxvY2tpbmcgZm9yIG1hdHJpeCwgY29tcGFyaXNvbnMgYWNyb3NzIGdyb3VwCmNvbG5hbWVzKGRlc2lnbikgPC0gbGV2ZWxzKGdyb3VwKQoKCiMgRml0IGEgbGluZWFyIG1vZGVsIHRvIHRoZSBkYXRhIC0tLS0KZml0IDwtIGxtRml0KHYuREVHTGlzdC5maWx0ZXJlZC5ub3JtLCBkZXNpZ24gPSBkZXNpZ24pCgojIEFzIGFuIGV4YW1wbGUsIGdlbmVyYXRlIGNvbXBhcmlzb24gbWF0cml4IGZvciBhIHBhaXJ3aXNlIGNvbXBhcmlzb24gLS0tLQojIGlMM3MgdnMgRkxGCiMgTm90ZSB0aGF0IHRoZSB0YXJnZXQvY29udHJhc3QgZ291cHMgd2lsbCBiZSBkaXZpZGVkIGJ5IHRoZSBudW1iZXIgb2YgbGlmZSAKIyBzdGFnZSBncm91cHMgZS5nLiBQRitGTEYvMiAtIGlMMytpTDNhK3BmTDErcHBMMStwcEwzLzUKY29tcGFyaXNvbiA8LSBjKCcoaUwzKS0oRkxGKScpCgp0YXJnZXRTdGFnZTwtIGNvbXBhcmlzb24gJT4lCiAgc3RyX3NwbGl0KHBhdHRlcm49Ii0iLCBzaW1wbGlmeSA9IFQpICU+JQogIC5bLDFdICU+JQogIGdzdWIoIigiLCAiIiwgLiwgZml4ZWQgPSBUUlVFKSAlPiUKICBnc3ViKCIpIiwgIiIsIC4sIGZpeGVkID0gVFJVRSkgJT4lCiAgc3RyX3NwbGl0KHBhdHRlcm4gPSAiXFwrIiwgc2ltcGxpZnkgPSBUKQoKY29udHJhc3RTdGFnZTwtY29tcGFyaXNvbiAlPiUKICBzdHJfc3BsaXQocGF0dGVybj0iLSIsIHNpbXBsaWZ5ID0gVCkgJT4lCiAgLlssMl0gJT4lCiAgZ3N1YigiKCIsICIiLCAuLCBmaXhlZCA9IFRSVUUpICU+JQogIGdzdWIoIikiLCAiIiwgLiwgZml4ZWQgPSBUUlVFKSAgJT4lCiAgc3RyX3NwbGl0KHBhdHRlcm4gPSAiXFwrIiwgc2ltcGxpZnkgPSBUKQoKY29tcGFyaXNvbjwtIHNhcHBseShzZXFfYWxvbmcoY29tcGFyaXNvbiksZnVuY3Rpb24oeCl7CiAgdFMgPC0gYXMudmVjdG9yKHRhcmdldFN0YWdlW3gsXSkgJT4lCiAgICAuWy4gIT0gIiJdIAogIGNTIDwtIGFzLnZlY3Rvcihjb250cmFzdFN0YWdlW3gsXSkgJT4lCiAgICAuWy4gIT0gIiJdIAogIHBhc3RlKHBhc3RlMCh0UywgCiAgICAgICAgICAgICAgIGNvbGxhcHNlID0gIisiKSAlPiUKICAgICAgICAgIHBhc3RlMCgiKCIsLiwiKS8iLGxlbmd0aCh0UykpLAogICAgICAgIHBhc3RlMChjUywgCiAgICAgICAgICAgICAgIGNvbGxhcHNlID0gIisiKSAlPiUKICAgICAgICAgIHBhc3RlMCgiKCIsLiwiKS8iLGxlbmd0aChjUykpLAogICAgICAgIHNlcCA9ICItIikKICAKfSkKCiMgR2VuZXJhdGUgY29udHJhc3QgbWF0cml4IC0tLS0KY29udHJhc3QubWF0cml4IDwtIG1ha2VDb250cmFzdHMoY29udHJhc3RzID0gY29tcGFyaXNvbiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzPWRlc2lnbikKCiMgZXh0cmFjdCB0aGUgbGluZWFyIG1vZGVsIGZpdCAtLS0tLQpmaXRzIDwtIGNvbnRyYXN0cy5maXQoZml0LCBjb250cmFzdC5tYXRyaXgpCiMgZW1waXJpY2FsIGJheWVzIHNtb290aGluZyBvZiBnZW5lLXdpc2Ugc3RhbmRhcmQgZGV2aWF0aW9ucyBwcm92aWRlcyBpbmNyZWFzZWQgcG93ZXIgKHNlZTogaHR0cHM6Ly93d3cuZGVncnV5dGVyLmNvbS9kb2kvMTAuMjIwMi8xNTQ0LTYxMTUuMTAyNykKZWJGaXQgPC0gZUJheWVzKGZpdHMpCgojIFB1bGwgb3V0IHRoZSBERUdzIHRoYXQgcGFzcyBhIHNwZWNpZmljIHRocmVzaG9sZCBmb3IgYWxsIHBhaXJ3aXNlIGNvbXBhcmlzb25zIC0tLS0KIyBBZGp1c3QgZm9yIG11bHRpcGxlIGNvbXBhcmlzb25zIHVzaW5nIG1ldGhvZCA9IGdsb2JhbC4gCnJlc3VsdHMgPC0gZGVjaWRlVGVzdHMoZWJGaXQsIG1ldGhvZD0iZ2xvYmFsIiwgYWRqdXN0Lm1ldGhvZD0iQkgiLCBwLnZhbHVlID0gYWRqLlAudGhyZXNoKQoKcmVjb2RlMDE8LSBmdW5jdGlvbih4KXsKICBjYXNlX3doZW4oeCA9PSAxIH4gIlVwIiwKICAgICAgICAgICAgeCA9PSAtMSB+ICJEb3duIiwKICAgICAgICAgICAgeCA9PSAwIH4gIk5vdFNpZyIpCn0KZGlmZkRlc2MgPC0gcmVzdWx0cyAlPiUKICBhc190aWJibGUocm93bmFtZXMgPSAiZ2VuZUlEIikgJT4lCiAgZHBseXI6Om11dGF0ZShhY3Jvc3MoLWdlbmVJRCwgdW5jbGFzcykpICU+JQogIGRwbHlyOjptdXRhdGUoYWNyb3NzKHdoZXJlKGlzLmRvdWJsZSksIHJlY29kZTAxKSkKCiMgRnVuY3Rpb24gdGhhdCBpZGVudGlmaWVzIHRvcCBERUdzIGJldHdlZW4gYSBzcGVjaWZpYyBjb250cmFzdCAtLS0tCmNhbGNfREVHX3RibCA8LSBmdW5jdGlvbiAoZWJGaXQsIGNvZWYpIHsKICBteVRvcEhpdHMuZGYgPC0gbGltbWE6OnRvcFRhYmxlKGViRml0LCBhZGp1c3QgPSJCSCIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29lZj1jb2VmLCBudW1iZXI9NDAwMDAsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc29ydC5ieT0ibG9nRkMiKSAlPiUKICAgIGFzX3RpYmJsZShyb3duYW1lcyA9ICJnZW5lSUQiKSAlPiUKICAgIGRwbHlyOjpyZW5hbWUodFN0YXRpc3RpYyA9IHQsIExvZ09kZHMgPSBCLCBCSC5hZGouUC5WYWwgPSBhZGouUC5WYWwpICU+JQogICAgZHBseXI6OnJlbG9jYXRlKFVuaVByb3RLQiwgRGVzY3JpcHRpb24sIEludGVyUHJvLCBHT190ZXJtLCBXQmdlbmVJRCwgU3RyX2dlbmVJRCwgU3RyX3BlcmNlbnRfaG9tb2xvZ3ksIENlX2dlbmVJRCwgQ2VfcGVyY2VudF9ob21vbG9neSwgLmFmdGVyID0gTG9nT2RkcykKICAKICBteVRvcEhpdHMuZGYKfQoKbGlzdC5teVRvcEhpdHMuZGYgPC0gc2FwcGx5KGNvbXBhcmlzb24sIGZ1bmN0aW9uKHkpewogIGNhbGNfREVHX3RibChlYkZpdCwgeSl9LCAKICBzaW1wbGlmeSA9IEZBTFNFLCAKICBVU0UuTkFNRVMgPSBUUlVFKQoKbGlzdC5teVRvcEhpdHMuZGYgPC0gc2FwcGx5KGNvbXBhcmlzb24sIGZ1bmN0aW9uKHkpewogIGxpc3QubXlUb3BIaXRzLmRmW1t5XV0gJT4lCiAgICBkcGx5cjo6c2VsZWN0KGdlbmVJRCwgCiAgICAgICAgICAgICAgICAgIGxvZ0ZDLCAKICAgICAgICAgICAgICAgICAgQkguYWRqLlAuVmFsOkNlX3BlcmNlbnRfaG9tb2xvZ3kpfSwKICBzaW1wbGlmeSA9IEZBTFNFLCAKICBVU0UuTkFNRVMgPSBUUlVFKQoKIyBHZXQgbG9nMkNQTSB2YWx1ZXMgYW5kIHRocmVzaG9sZCBpbmZvcm1hdGlvbiBmb3IgZ2VuZXMgb2YgaW50ZXJlc3QKbGlzdC5teVRvcEhpdHMuZGYgPC0gc2FwcGx5KHNlcV9hbG9uZyhjb21wYXJpc29uKSwgZnVuY3Rpb24oeSl7CiAgdFM8LSB0YXJnZXRTdGFnZVt5LF1bdGFyZ2V0U3RhZ2VbeSxdIT0iIl0KICBjUzwtIGNvbnRyYXN0U3RhZ2VbeSxdW2NvbnRyYXN0U3RhZ2VbeSxdIT0iIl0KICAKICBjb25jYXRfbmFtZSA8LSBmdW5jdGlvbih4KSB7CiAgICBpZmVsc2UoeCA9PSAidGFyZ2V0IiwgCiAgICAgICAgICAgcGFzdGUodFMsIGNvbGxhcHNlID0gIisiKSwgCiAgICAgICAgICAgcGFzdGUoY1MsIGNvbGxhcHNlID0gIisiKSkKICB9CiAgCiAgZ3JvdXBBdmdzIDwtIGRpZmZHZW5lcy5kZiAlPiUKICAgIGRwbHlyOjpzZWxlY3QoZ2VuZUlELCBzdGFydHNfd2l0aChwYXN0ZTAodFMsIi0iKSksIAogICAgICAgICAgICAgICAgICBzdGFydHNfd2l0aChwYXN0ZTAoY1MsIi0iKSkpICU+JQogICAgcGl2b3RfbG9uZ2VyKGNvbHMgPSAtZ2VuZUlELCBuYW1lc190byA9IGMoImdyb3VwIiwic2FtcGxlIiksIHZhbHVlc190byA9ICJDUE0iLAogICAgICAgICAgICAgICAgIG5hbWVzX3NlcCA9ICItIikgJT4lCiAgICBkcGx5cjo6bXV0YXRlKGNvbnRyYXN0SUQgPSBpZl9lbHNlKGdyb3VwICVpbiUgdFMsInRhcmdldCIsICJjb250cmFzdCIpKSAlPiUKICAgIGdyb3VwX2J5KGdlbmVJRCwgY29udHJhc3RJRCkgJT4lCiAgICBkcGx5cjo6c2VsZWN0KC1zYW1wbGUpICU+JQogICAgc3VtbWFyaXplKG1lYW4gPSBtZWFuKENQTSksIC5ncm91cHMgPSAiZHJvcF9sYXN0IikgJT4lCiAgICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gY29udHJhc3RJRCwgdmFsdWVzX2Zyb20gPSBtZWFuKSAlPiUKICAgIGRwbHlyOjpyZWxvY2F0ZShjb250cmFzdCwgLmFmdGVyID0gdGFyZ2V0KSAlPiUKICAgIGRwbHlyOjpyZW5hbWVfd2l0aChjb25jYXRfbmFtZSwgLWdlbmVJRCkgJT4lCiAgICBkcGx5cjo6cmVuYW1lX3dpdGgoLmNvbHMgPS1nZW5lSUQsIC5mbiA9IH4gcGFzdGUwKCJhdmdfKCIsLngsIikiKSkKICAKICBkaWZmR2VuZXMuZGYgJT4lCiAgICBkcGx5cjo6c2VsZWN0KGdlbmVJRCwgc3RhcnRzX3dpdGgocGFzdGUwKHRTLCItIikpLCAKICAgICAgICAgICAgICAgICAgc3RhcnRzX3dpdGgocGFzdGUwKGNTLCItIikpKSAlPiUKICAgIGxlZnRfam9pbihncm91cEF2Z3MsIGJ5ID0gImdlbmVJRCIpICU+JQogICAgbGVmdF9qb2luKGxpc3QubXlUb3BIaXRzLmRmW1t5XV0sLiwgYnkgPSAiZ2VuZUlEIikgJT4lCiAgICBsZWZ0X2pvaW4oZHBseXI6OnNlbGVjdChkaWZmRGVzYyxnZW5lSUQsY29tcGFyaXNvblt5XSksIGJ5ID0gImdlbmVJRCIpICU+JQogICAgZHBseXI6OnJlbmFtZShERUdfRGVzYz1jb21wYXJpc29uW3ldKSAlPiUKICAgIGRwbHlyOjpyZWxvY2F0ZShERUdfRGVzYykgJT4lCiAgICBkcGx5cjo6cmVsb2NhdGUobG9nRkM6Q2VfcGVyY2VudF9ob21vbG9neSwgLmFmdGVyID0gbGFzdF9jb2woKSkKICAKfSwKc2ltcGxpZnkgPSBGQUxTRSkKCmNvbXBhcmlzb24gPC0gZ3N1YigiL1swLTldKiIsIiIsIGNvbXBhcmlzb24pCm5hbWVzKGxpc3QubXlUb3BIaXRzLmRmKSA8LSBjb21wYXJpc29uCgpsaXN0Lm15VG9wSGl0cy5kZiA8LSBzYXBwbHkoY29tcGFyaXNvbiwgZnVuY3Rpb24oeSl7CiAgbGlzdC5teVRvcEhpdHMuZGZbW3ldXSAlPiUKICAgIGRwbHlyOjptdXRhdGUoREVHX0Rlc2MgPSBjYXNlX3doZW4oREVHX0Rlc2MgPT0gIlVwIiB+IHBhc3RlMCgiVXAgaW4gIiwgc3RyX3NwbGl0KHksJy0nLHNpbXBsaWZ5ID0gVClbMSwxXSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIERFR19EZXNjID09ICJEb3duIiB+IHBhc3RlMCgiRG93biBpbiAiLCBzdHJfc3BsaXQoeSwnLScsc2ltcGxpZnkgPSBUKVsxLDFdKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgREVHX0Rlc2MgPT0gIk5vdFNpZyIgfiAiTm90U2lnIikpIAp9LApzaW1wbGlmeSA9IEZBTFNFLCAKVVNFLk5BTUVTID0gVFJVRSkKCiMgUEMxIFZvbGNhbm8gUGxvdCBhbmQgSW50ZXJhY3RpdmUgVGFibGUgLS0tLQp2cGxvdDEgPC0gZ2dwbG90KGxpc3QubXlUb3BIaXRzLmRmW1sxXV0pICsKICBhZXMoeT0tbG9nMTAoQkguYWRqLlAuVmFsKSwgeD1sb2dGQywgdGV4dCA9IHBhc3RlKGdlbmVJRCwgIjxicj4iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvZ0ZDOiIsIHJvdW5kKGxvZ0ZDLCBkaWdpdHMgPSAyKSwgIjxicj4iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInAtdmFsOiIsIGZvcm1hdChCSC5hZGouUC5WYWwsIGRpZ2l0cyA9IDMsIHNjaWVudGlmaWMgPSBUUlVFKSkpICsKICBnZW9tX3BvaW50KHNpemU9MikgKwogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IC1sb2cxMChhZGouUC50aHJlc2gpLCAKICAgICAgICAgICAgIGxpbmV0eXBlPSJsb25nZGFzaCIsIAogICAgICAgICAgICAgY29sb3VyPSJncmV5IiwgCiAgICAgICAgICAgICBzaXplPTEpICsgCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gbGZjLnRocmVzaCwgCiAgICAgICAgICAgICBsaW5ldHlwZT0ibG9uZ2Rhc2giLCAKICAgICAgICAgICAgIGNvbG91cj0iI0JFNjg0RCIsIAogICAgICAgICAgICAgc2l6ZT0xKSArCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gLWxmYy50aHJlc2gsIAogICAgICAgICAgICAgbGluZXR5cGU9ImxvbmdkYXNoIiwgCiAgICAgICAgICAgICBjb2xvdXI9IiMyQzQ2N0EiLCAKICAgICAgICAgICAgIHNpemU9MSkgKwogIGxhYnModGl0bGUgPSBwYXN0ZTAoJ1BhaXJ3aXNlIENvbXBhcmlzb246ICcsCiAgICAgICAgICAgICAgICAgICAgICBnc3ViKCctJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgJyB2cyAnLAogICAgICAgICAgICAgICAgICAgICAgICAgICBjb21wYXJpc29uWzFdKSksCiAgICAgICBzdWJ0aXRsZSA9IHBhc3RlMCgiZ3JleSBsaW5lOiBwID0gIiwKICAgICAgICAgICAgICAgICAgICAgICAgIGFkai5QLnRocmVzaCwgIjsgY29sb3JlZCBsaW5lczogbG9nLWZvbGQgY2hhbmdlID0gIiwgbGZjLnRocmVzaCksCiAgICAgICBjb2xvciA9ICJHZW5lSURzIikgKwogIHRoZW1lX1B1YmxpY2F0aW9uKCkgCnZwbG90MQoKIyBJbnRlcmFjdGl2ZSBUYWJsZXMKeXk8LSAxCnRTPC0gdGFyZ2V0U3RhZ2VbeXksXVt0YXJnZXRTdGFnZVt5eSxdIT0iIl0KY1M8LSBjb250cmFzdFN0YWdlW3l5LF1bY29udHJhc3RTdGFnZVt5eSxdIT0iIl0Kc2FtcGxlLm51bS50UyA8LSBzYXBwbHkodFMsIGZ1bmN0aW9uKHgpIHtjb2xTdW1zKHYuREVHTGlzdC5maWx0ZXJlZC5ub3JtJGRlc2lnbilbW3hdXX0pICU+JSBzdW0oKQpzYW1wbGUubnVtLmNTIDwtIHNhcHBseShjUywgZnVuY3Rpb24oeCkge2NvbFN1bXModi5ERUdMaXN0LmZpbHRlcmVkLm5vcm0kZGVzaWduKVtbeF1dfSkgJT4lIHN1bSgpCgoKbl9udW1fY29scyA8LSBzYW1wbGUubnVtLnRTICsgc2FtcGxlLm51bS5jUyArIDUKCgpMUy5kYXRhdGFibGUgPC0gbGlzdC5teVRvcEhpdHMuZGZbW3l5XV0gJT4lCiAgRFQ6OmRhdGF0YWJsZShyb3duYW1lcyA9IEZBTFNFLAogICAgICAgICAgICAgICAgY2FwdGlvbiA9IGh0bWx0b29sczo6dGFncyRjYXB0aW9uKAogICAgICAgICAgICAgICAgICBzdHlsZSA9ICdjYXB0aW9uLXNpZGU6IHRvcDsgdGV4dC1hbGlnbjogbGVmdDsgY29sb3I6IGJsYWNrJywKICAgICAgICAgICAgICAgICAgaHRtbHRvb2xzOjp0YWdzJGIoJ0RpZmZlcmVudGlhbGx5IEV4cHJlc3NlZCBHZW5lcyBpbicsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBodG1sdG9vbHM6OnRhZ3MkZW0oJ1MuIHJhdHRpJyksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBnc3ViKCctJywnIHZzICcsY29tcGFyaXNvblt5eV0pKSwKICAgICAgICAgICAgICAgICAgaHRtbHRvb2xzOjp0YWdzJGJyKCksCiAgICAgICAgICAgICAgICAgICJUaHJlc2hvbGQ6IHAgPCAiLAogICAgICAgICAgICAgICAgICBhZGouUC50aHJlc2gsICI7IGxvZy1mb2xkIGNoYW5nZSA+ICIsCiAgICAgICAgICAgICAgICAgIGxmYy50aHJlc2gsCiAgICAgICAgICAgICAgICAgIGh0bWx0b29sczo6dGFncyRicigpLAogICAgICAgICAgICAgICAgICAnVmFsdWVzID0gbG9nMiBjb3VudHMgcGVyIG1pbGxpb24nKSwKICAgICAgICAgICAgICAgIG9wdGlvbnMgPSBsaXN0KGF1dG9XaWR0aCA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzY3JvbGxYID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNjcm9sbFkgPSAnMzAwcHgnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2Nyb2xsQ29sbGFwc2UgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb3JkZXIgPSBsaXN0KG5fbnVtX2NvbHMtMSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ2Rlc2MnKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlYXJjaEhpZ2hsaWdodCA9IFRSVUUsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFnZUxlbmd0aCA9IDI1LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxlbmd0aE1lbnUgPSBjKCI1IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICIxMCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiMjUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjUwIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICIxMDAiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbHVtbkRlZnMgPSBsaXN0KAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsaXN0KAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRhcmdldHMgPSAoKG5fbnVtX2NvbHMgKyAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDEpKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZW5kZXIgPSBKUygKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJmdW5jdGlvbihkYXRhLCByb3cpIHsiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImRhdGEudG9FeHBvbmVudGlhbCgxKTsiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIn0iKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsaXN0KAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRhcmdldHMgPSAoKG5fbnVtX2NvbHMgKyAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDQpOihuX251bV9jb2xzICsgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA1KSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVuZGVyID0gSlMoCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiZnVuY3Rpb24oZGF0YSwgdHlwZSwgcm93LCBtZXRhKSB7IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyZXR1cm4gdHlwZSA9PT0gJ2Rpc3BsYXknICYmIGRhdGEubGVuZ3RoID4gMjAgPyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiJzxzcGFuIHRpdGxlPVwiJyArIGRhdGEgKyAnXCI+JyArIGRhdGEuc3Vic3RyKDAsIDIwKSArICcuLi48L3NwYW4+JyA6IGRhdGE7IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ9IikKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGlzdCh0YXJnZXRzID0gIl9hbGwiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNsYXNzPSJkdC1yaWdodCIpCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcm93Q2FsbGJhY2sgPSBKUyhjKAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiZnVuY3Rpb24ocm93LCBkYXRhKXsiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiICBmb3IodmFyIGk9MDsgaTxkYXRhLmxlbmd0aDsgaSsrKXsiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiICAgIGlmKGRhdGFbaV0gPT09IG51bGwpeyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICIgICAgICAkKCd0ZDplcSgnK2krJyknLCByb3cpLmh0bWwoJ05BJykiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiICAgICAgICAuY3NzKHsnY29sb3InOiAncmdiKDE1MSwxNTEsMTUxKScsICdmb250LXN0eWxlJzogJ2l0YWxpYyd9KTsiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiICAgIH0iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiICB9IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIn0iICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkpCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICkpIApMUy5kYXRhdGFibGUgPC0gTFMuZGF0YXRhYmxlICU+JQogIERUOjpmb3JtYXRSb3VuZChjb2x1bW5zPWMoMzpuX251bV9jb2xzKSwgCiAgICAgICAgICAgICAgICAgIGRpZ2l0cz0zKQoKTFMuZGF0YXRhYmxlIDwtIExTLmRhdGF0YWJsZSAlPiUKICBEVDo6Zm9ybWF0Um91bmQoY29sdW1ucz1jKG5fbnVtX2NvbHMrMiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBuX251bV9jb2xzKzksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBuX251bV9jb2xzKzExKSwgCiAgICAgICAgICAgICAgICAgIGRpZ2l0cz0yKQoKTFMuZGF0YXRhYmxlIDwtIExTLmRhdGF0YWJsZSAlPiUKICBEVDo6Zm9ybWF0U2lnbmlmKGNvbHVtbnM9YyhuX251bV9jb2xzKzEpLCAKICAgICAgICAgICAgICAgICAgIGRpZ2l0cz0zKQoKTFMuZGF0YXRhYmxlCgpgYGAKCiMgQmVuY2htYXJraW5nCmBgYHtyIGJlbmNobWFya2luZ30KCnN1cHByZXNzUGFja2FnZVN0YXJ0dXBNZXNzYWdlcyh7CiAgbGlicmFyeShvcGVueGxzeCkKICBsaWJyYXJ5KHRpZHl2ZXJzZSkKICBsaWJyYXJ5KGdncGxvdDIpCn0pCiMgTG9hZCBIdW50IERhdGFzZXQ6IGlMMyB2cyBGTEYgY29tcGFyaXNvbgp0ZW1wLmRhdCA8LSAgcmVhZC54bHN4ICgiLi4vQmVuY2htYXJraW5nL25nLjM0OTUtUzUueGxzeCIsIAogICAgICAgICAgICAgICAgICAgICAgICBzaGVldCA9IDIsIHN0YXJ0Um93ID0gMikKCkh1bnQuZGF0IDwtIHRpYmJsZShnZW5lSUQgPSB0ZW1wLmRhdCRYMiwgbG9nRkMgPSB0ZW1wLmRhdCRsb2dGQykKcm0odGVtcC5kYXQpCgojIFJlbmFtZSBSZXN1bHRzIG9mIGlMMyB2cyBGTEYgY29tcGFyaXNvbiBmcm9tIEJyb3dzZXIKQnJvd3Nlci5kYXQgPC0gbGlzdC5teVRvcEhpdHMuZGYkYChpTDMpLShGTEYpYCAlPiUKICBkcGx5cjo6c2VsZWN0KGdlbmVJRCwgbG9nRkMpCgpwcmludChwYXN0ZSgnVG90YWwgbnVtYmVyIG9mIGdlbmVzIGluIEh1bnQgKmV0IGFsKiAyMDE2IGlMMyB2cyBGTEYgY29tcGFyaXNvbiB0YWI6Jyxucm93KEh1bnQuZGF0KSkpCnByaW50KHBhc3RlKCdUb3RhbCBudW1iZXIgb2YgZ2VuZXMgaW4gU3RyLVJOQXNlcSBCcm93c2VyIGlMMyB2cyBGTEYgb3V0cHV0IGZpbGU6JywgbnJvdyhCcm93c2VyLmRhdCkpKSAKCgojIFRoZSBwbG90IGJlbG93IHRha2VzIHRoZSBnZW5lcyB3aXRoIExvZ0ZDIHJlc3VsdHMgaW4gYm90aCB0aGUgQnJvd3NlciBhbmQgSHVudCBkYXRhYmFzZXMsIGFuZCBwbG90cyB0aGUgdHdvIHNldHMgYWdhaW5zdCBlYWNoIG90aGVyLiAKcGxvdHRpbmcuYWxsIDwtIGlubmVyX2pvaW4oQnJvd3Nlci5kYXQsIEh1bnQuZGF0LCBieSA9ICJnZW5lSUQiKQpnZ3Bsb3QocGxvdHRpbmcuYWxsLCBhZXMoeCA9IGxvZ0ZDLngsIHkgPSBsb2dGQy55KSkgKwogIGdlb21fc21vb3RoKG1ldGhvZD1sbSwgY29sb3IgPSAncmVkJywgZm9ybXVsYSA9ICJ5IH4geCIpICsKICBnZW9tX3BvaW50KHNoYXBlPTE2LCBzaXplPTMsIGFscGhhID0gMC44KSArCiAgbGFicyh0aXRsZSA9ICJBbGwgT3ZlcmxhcHBpbmcgR2VuZXM6IFN0ci1Ccm93c2VyIHZzIEh1bnQgRGF0YSIsCiAgICAgICBzdWJ0aXRsZSA9ICJpTDMgdnMgRkxGIiwKICAgICAgIGNhcHRpb24gPSAicG9pbnRzID0gZ2VuZXM7IHJlZCBsaW5lID0gbGluZWFyIG1vZGVsICh5IH4geCkiLAogICAgICAgeCA9ICJTdHItQnJvd3NlciBMb2dGQyIsCiAgICAgICB5ID0gIkh1bnQgTG9nRkMiKSArCiAgY29vcmRfZXF1YWwoKSArCiAgdGhlbWVfYncoKQoKCmBgYAoKCiMgQ2x1c3RlciBERUdzIGludG8gZnVuY3Rpb25hbCBtb2R1bGVzIC0tLQpgYGB7ciBoZWF0bWFwcywgZXZhbD1GQUxTRSwgaW5jbHVkZT1GQUxTRX0KIyBJbnRyb2R1Y3Rpb24gdG8gdGhpcyBjaHVuayAtLS0tCiMgdGhpcyBjaHVuayBjcmVhdGVzIGhlYXRtYXBzIGZyb20gZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIGdlbmVzOwojIGl0IHRha2VzIGFzIGlucHV0IGEgbGlzdCBvZiBnZW5lcyB0aGF0IGFyZSBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgaW4gYW55IGxpZmUgc3RhZ2UKIyBJdCBzZWxlY3RzIG1vZHVsZXMgb2YgY28tZXhwcmVzc2VkIGdlbmVzIGJhc2VkIG9uIHBlYXJzb24gY29ycmVsYXRpb25zCiMgCiMgVGhlc2UgZGF0YS9yZXN1bHRzIGFyZSBleGFtcGxlcyBvZiBwb3NzaWJsZSBhbmFseXNlcyB0aGF0IGNhbiBiZSBydW4gb24gdGhpcyBkYXRhLgoKIyBMb2FkIHBhY2thZ2VzIC0tLS0tCnN1cHByZXNzUGFja2FnZVN0YXJ0dXBNZXNzYWdlcyh7CiAgbGlicmFyeSh0aWR5dmVyc2UpCiAgbGlicmFyeShsaW1tYSkKICBsaWJyYXJ5KFJDb2xvckJyZXdlcikKICBsaWJyYXJ5KGdwbG90cykKICBsaWJyYXJ5KGhlYXRtYXBseSkKICBsaWJyYXJ5KGdncGxvdDIpCiAgbGlicmFyeShlZ2cpCiAgbGlicmFyeShkZW5kZXh0ZW5kKQogIHNvdXJjZSgiLi4vU3Ryb25neWxvaWRlc19STkFzZXFfQnJvd3Nlci9TZXJ2ZXIvZ2doZWF0bWFwX2xvY2FsLlIiKQp9KQoKIyBDaG9vc2UgYSBjb2xvciBwYWxsZXR0ZSAtLS0tCiNteWhlYXRjb2xvcnMgPC0gcmV2KGJyZXdlci5wYWwobmFtZT0iUmRCdSIsIG49MTEpKQpteWhlYXRjb2xvcnMgPC0gUmRCdSg3NSkKCiMgU2VsZWN0IHRoZSBjb21wYXJpc29uCnkgPSAxCgojIEdlbmVyYXRlIHZhcmlhYmxlIGNvbnRhaW5pbmcgZXhwcmVzc2lvbiBkYXRhIGZvciB0aGUgdGhyZXNob2xkZWQgREVHcyAKZGlmZkdlbmVzLnRocmVzaCA8LSB2LkRFR0xpc3QuZmlsdGVyZWQubm9ybSRFW3Jlc3VsdHNbLHldICE9MCxdCgoKIyBDbHVzdGVyIERFR3MgYWNyb3NzIHN0YWdlcyAtLS0tCiNiZWdpbiBieSBjbHVzdGVyaW5nIHRoZSBnZW5lcyAocm93cykgZm9yIGEgbGlzdCBvZiBnZW5lcyB0aGF0IGFyZSBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgaW4gYXQgbGVhc3Qgb25lIGxpZmUgc3RhZ2UKIyB1c2UgdGhlICdjb3InIGZ1bmN0aW9uIGFuZCB0aGUgcGVhcnNvbiBtZXRob2QgZm9yIGZpbmRpbmcgYWxsIHBhaXJ3aXNlIGNvcnJlbGF0aW9ucyBvZiBnZW5lcwojICcxLWNvcicgY29udmVydHMgdGhpcyB0byBhIDAtMiBzY2FsZSBmb3IgZWFjaCBvZiB0aGVzZSBjb3JyZWxhdGlvbnMsIHdoaWNoIGNhbiB0aGVuIGJlIHVzZWQgdG8gY2FsY3VsYXRlIGEgZGlzdGFuY2UgbWF0cml4IHVzaW5nICdhcy5kaXN0JwpjbHVzdFJvd3MgPC0gaGNsdXN0KGFzLmRpc3QoMS1jb3IodChkaWZmR2VuZXMudGhyZXNoKSwgbWV0aG9kPSJwZWFyc29uIikpLCBtZXRob2Q9ImNvbXBsZXRlIikgCiMgaGllcmFyY2hpY2FsIGNsdXN0ZXJpbmcgaXMgYSB0eXBlIG9mIHVuc3VwZXJ2aXNlZCBjbHVzdGVyaW5nLiAKIyBOT1RFOiB0aGlzIGNsdXN0ZXIgbWF5IHByb3ZpZGUgZGlmZmVyZW50IHJlc3VsdHMgdG8gb25lIGJhc2VkIG9uIGxvZzIuY3BtLmZpbHRlcmVkLm5vcm0gZGF0YSwgbGlrZWx5IGIvYyB0aGlzIHZlcnNpb24gaXMgc3BlY2lmY2FsbHkgZm9jdXNlZCBvbiBnZW5lcyB0aGF0IGFyZSBzaWduaWZpY2FudGx5IGRpZmZlcmVudCBiZXR3ZWVuIGNvbmRpdGlvbnMuCiMgUmVsYXRlZCBtZXRob2RzIGluY2x1ZGUgSy1tZWFucywgU09NLCBldGMgCiMgdW5zdXBlcnZpc2VkIG1ldGhvZHMgYXJlIGJsaW5kIHRvIHNhbXBsZS9ncm91cCBpZGVudGl0eQojIGluIGNvbnRyYXN0LCBzdXBlcnZpc2VkIG1ldGhvZHMgJ3RyYWluJyBvbiBhIHNldCBvZiBsYWJlbGVkIGRhdGEuICAKIyBzdXBlcnZpc2VkIGNsdXN0ZXJpbmcgbWV0aG9kcyBpbmNsdWRlIHJhbmRvbSBmb3Jlc3QsIGFuZCBhcnRpZmljaWFsIG5ldXJhbCBuZXR3b3JrcwoKIyBjbHVzdGVyIHNhbXBsZXMgKGNvbHVtbnMpCmNsdXN0Q29sdW1ucyA8LSBoY2x1c3QoYXMuZGlzdCgxLWNvcihkaWZmR2VuZXMudGhyZXNoLCBtZXRob2Q9InNwZWFybWFuIikpLCBtZXRob2Q9ImNvbXBsZXRlIikgI2NsdXN0ZXIgY29sdW1ucyBieSBzcGVhcm1hbiBjb3JyZWxhdGlvbgojbm90ZTogdXNlIFNwZWFybWFuLCBpbnN0ZWFkIG9mIFBlYXJzb24sIGZvciBjbHVzdGVyaW5nIHNhbXBsZXMgYmVjYXVzZSBpdCBnaXZlcyBlcXVhbCB3ZWlnaHQgdG8gaGlnaGx5IHZzIGxvd2x5IGV4cHJlc3NlZCB0cmFuc2NyaXB0cyBvciBnZW5lcwoKI0N1dCB0aGUgcmVzdWx0aW5nIHRyZWUgYW5kIGNyZWF0ZSBjb2xvciB2ZWN0b3IgZm9yIGNsdXN0ZXJzLiAgCm1vZHVsZS5hc3NpZ24gPC0gc3RhdHM6OmN1dHJlZShjbHVzdFJvd3MsIGs9OCkgI1RoZSBkaWZmR2VuZXMgaW5mbyBpcyBiYXNlZCBvbiBhIHBhaXJ3aXNlIGNvbXBhcmlzb24gYmV0d2VlbiBhbGwgNyBsaWZlIHN0YWdlcy4gCgojIGFzc2lnbiBhIGNvbG9yIHRvIGVhY2ggbW9kdWxlIChtYWtlcyBpdCBlYXN5IHRvIGlkZW50aWZ5IGFuZCBtYW5pcHVsYXRlKQptb2R1bGUuY29sb3IgPC0gcmFpbmJvdyhsZW5ndGgodW5pcXVlKG1vZHVsZS5hc3NpZ24pKSwgc3RhcnQ9MC4xLCBlbmQ9MC45KSAKbW9kdWxlLmNvbG9yIDwtIG1vZHVsZS5jb2xvclthcy52ZWN0b3IobW9kdWxlLmFzc2lnbildIAoKIyAjIHNpbXBsZnkgaGVhdG1hcCBieSBhdmVyYWdpbmcgdGhlIGJpb2xvZ2ljYWwgcmVwbGljYXRlcyBhbmQgZGlzcGxheSBvbmx5IG9uZSBjb2x1bW4gcGVyIGNvbmRpdGlvbgojIGRpZmZHZW5lcy5BVkcgPC0gYXZlYXJyYXlzKGRpZmZHZW5lcy50aHJlc2gpCgojIHBsb3QgdGhlIGhjbHVzdCByZXN1bHRzIGFzIGEgaGVhdG1hcCwgZ3JvdXBpbmcgdGhlIGxpZmUgc3RhZ2VzIHRvZ2V0aGVyCmRpZmZHZW5lcy5oZWF0bWFwIDwtIGhlYXRtYXAuMihkaWZmR2VuZXMudGhyZXNoLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3J0Q29sID0gMCwgYWRqQ29sPSBjKDAuNSwwLjUpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgUm93dj1hcy5kZW5kcm9ncmFtKGNsdXN0Um93cyksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBDb2x2PWFzLmRlbmRyb2dyYW0oY2x1c3RDb2x1bW5zKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGtleS50aXRsZSA9IE5BLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWFpbiA9IHBhc3RlMCgiREVHIEhlYXRtYXAgKGJ5IGxpZmUgc3RhZ2UpOiAiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN1YiA9IHBhc3RlMCgiR2VuZXMgcGFzcyB0aHJlc2hvbGQgaW4gPj0gMSBjb21wYXJpc29uLiBUaHJlc2hvbGQ6IHAgPCAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFkai5QLnRocmVzaCwgIjsgbG9nLWZvbGQgY2hhbmdlID4gIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZmMudGhyZXNoKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFJvd1NpZGVDb2xvcnM9bW9kdWxlLmNvbG9yLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sPXJldihteWhlYXRjb2xvcnMpLCBzY2FsZT0ncm93JywgbGFiUm93PU5BLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVuc2l0eS5pbmZvPSJub25lIiwgdHJhY2U9Im5vbmUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2V4Um93PTEsIGNleENvbD0xKQoKIyMgR0dQbG90cyB2ZXJzaW9uCiMgZ2cuZGlmZkdlbmVzLmhlYXRtYXA8LWdnaGVhdG1hcF9sb2NhbChkaWZmR2VuZXMudGhyZXNoLAojICAgICAgICAgICAgICAgICAgICBjb2xvcnMgPSByZXYobXloZWF0Y29sb3JzKSwKIyAgICAgICAgICAgICAgICAgICAgUm93dj0gbGFkZGVyaXplKGFzLmRlbmRyb2dyYW0oY2x1c3RSb3dzKSksCiMgICAgICAgICAgICAgICAgICAgIENvbHY9bGFkZGVyaXplKGFzLmRlbmRyb2dyYW0oY2x1c3RDb2x1bW5zKSksCiMgICAgICAgICAgICAgICAgICAgIGtleS50aXRsZSA9ICJMb2cyQ1BNIiwKIyAgICAgICAgICAgICAgICAgICAgYnJhbmNoZXNfbHdkID0gMC4yLAojICAgICAgICAgICAgICAgICAgICBzaG93dGlja2xhYmVscyA9IGMoVFJVRSwgRkFMU0UpLAojICAgICAgICAgICAgICAgICAgICBzY2FsZT0ncm93JywKIyAgICAgICAgICAgICAgICAgICAgY2V4Um93PTEsIGNleENvbD0xKQoKIyBnZ3NhdmUoIi4vaGVhdG1hcC5wZGYiLCBwbG90ID0gZ2cuaGVhdG1hcCwgd2lkdGggPSAxMSwgaGVpZ2h0ID0gOCwgdW5pdHMgPSAiaW4iLCBkZXZpY2UgPSAicGRmIikKIyBNYWtlIGFuIGludGVyYWN0aXZlIHZlcnNpb24KIyBpbnRlcmFjdGl2ZS5kaWZmR2VuZXMuaGVhdG1hcCA8LSBoZWF0bWFwbHkoZGlmZkdlbmVzLnRocmVzaCwKIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvcnMgPSByZXYobXloZWF0Y29sb3JzKSwKIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBSb3d2PSBsYWRkZXJpemUoYXMuZGVuZHJvZ3JhbShjbHVzdFJvd3MpKSwKIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBDb2x2PWxhZGRlcml6ZShhcy5kZW5kcm9ncmFtKGNsdXN0Q29sdW1ucykpLAojICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNob3d0aWNrbGFiZWxzID0gYyhUUlVFLCBGQUxTRSksCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2NhbGU9J3JvdycsCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGxvdF9tZXRob2QgPSAiZ2dwbG90IiwKIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBicmFuY2hlc19sd2QgPSAwLjIsCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAga2V5LnRpdGxlID0gIkxvZzJDUE0iLAojICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNleFJvdz0xLCBjZXhDb2w9MSkKCmBgYAoKIyBGdW5jdGlvbmFsIEVucmljaG1lbnQgQW5hbHlzaXMgLS0tCmBgYHtyIGZ1bmN0aW9uYWxFbnJpY2htZW50fQojIExvYWQgcGFja2FnZXMgLS0tLQpzdXBwcmVzc1BhY2thZ2VTdGFydHVwTWVzc2FnZXMoewogIGxpYnJhcnkodGlkeXZlcnNlKQogIGxpYnJhcnkobGltbWEpCiAgbGlicmFyeShvcGVueGxzeCkKICBsaWJyYXJ5KGdwbG90cykgI2ZvciBoZWF0bWFwcwogIGxpYnJhcnkoRFQpICNpbnRlcmFjdGl2ZSBhbmQgc2VhcmNoYWJsZSB0YWJsZXMgb2Ygb3VyIEdTRUEgcmVzdWx0cwogIGxpYnJhcnkoR1NFQUJhc2UpICNmdW5jdGlvbnMgYW5kIG1ldGhvZHMgZm9yIEdlbmUgU2V0IEVucmljaG1lbnQgQW5hbHlzaXMKICBsaWJyYXJ5KEJpb2Jhc2UpICNiYXNlIGZ1bmN0aW9ucyBmb3IgYmlvY29uZHVjdG9yOyByZXF1aXJlZCBieSBHU0VBQmFzZQogIGxpYnJhcnkoR1NWQSkgI0dlbmUgU2V0IFZhcmlhdGlvbiBBbmFseXNpcywgYSBub24tcGFyYW1ldHJpYyBhbmQgdW5zdXBlcnZpc2VkIG1ldGhvZCBmb3IgZXN0aW1hdGluZyB2YXJpYXRpb24gb2YgZ2VuZSBzZXQgZW5yaWNobWVudCBhY3Jvc3Mgc2FtcGxlcy4KICBsaWJyYXJ5KGdwcm9maWxlcjIpICN0b29scyBmb3IgYWNjZXNzaW5nIHRoZSBHTyBlbnJpY2htZW50IHJlc3VsdHMgdXNpbmcgZzpQcm9maWxlciB3ZWIgcmVzb3VyY2VzCiAgbGlicmFyeShjbHVzdGVyUHJvZmlsZXIpICMgcHJvdmlkZXMgYSBzdWl0ZSBvZiB0b29scyBmb3IgZnVuY3Rpb25hbCBlbnJpY2htZW50IGFuYWx5c2lzCiAgbGlicmFyeShtc2lnZGJyKSAjIGFjY2VzcyB0byBtc2lnZGIgY29sbGVjdGlvbnMgZGlyZWN0bHkgd2l0aGluIFIKICBsaWJyYXJ5KGVucmljaHBsb3QpICMgZ3JlYXQgZm9yIG1ha2luZyB0aGUgc3RhbmRhcmQgR1NFQSBlbnJpY2htZW50IHBsb3RzCn0pCiMgUGljayBhIHBhaXJ3aXNlIGNvbXBhcmlzb24KeXkgPC0gMQoKIyBDYXJyeSBvdXQgR08gZW5yaWNobWVudCB1c2luZyBnUHJvZmlsZXIyIC0tLS0KIyBHTyBlbnJpY2htZW50IHJlcXVpcmVzIGEgcHJlLXNlbGVjdGVkIHNldCBvZiBnZW5lcy4gQ2FuIHVzZSBtdWx0aXBsZSBjcml0ZXJpYSB0byBkbyB0aGF0IGluaXRpYWwgc2VsZWN0aW9uLgojIFRoZSBHTyB0ZXJtcyBJJ20gYWNjZXNzaW5nIHVzaW5nIHRoZSBnb3N0IGFyZSBmcm9tIEh1bnQgZXQgYWwgMjAxNiwgSSBiZWxpZXZlLgoKIyAjIFBDMSBUb3BUYWJsZSBSZXN1bHRzCiMgZW5yaWNoZWQuc2V0LnBvcyA8LWxpc3QubXlUb3BIaXRzLmRmW1t5eV1dICU+JSAKIyAgICAgc2xpY2VfbWF4KGxvZ0ZDLCBwcm9wID0gLjEpICMgZ2V0IHRvcCAxMCUgb2YgZ2VuZXMKIyAKIyBlbnJpY2hlZC5zZXQubmVnIDwtIGxpc3QubXlUb3BIaXRzLmRmW1t5eV1dICU+JSAKIyAgICAgc2xpY2VfbWluKGxvZ0ZDLCBwcm9wID0gLjEpICMgZ2V0IHRvcCAxMCUgb2YgZ2VuZXMKIyAKIyBnb3N0LnJlcy5wb3MgPC0gZ29zdChsaXN0KFRhcmdldF9VcHJlZ3VsYXRlZCA9IGVucmljaGVkLnNldC5wb3MkZ2VuZUlEKSwgb3JnYW5pc20gPSAic3RzdGVycHJqZWI1MjgiLCBjb3JyZWN0aW9uX21ldGhvZCA9ICJmZHIiKQojIGdvc3RwbG90KGdvc3QucmVzLnBvcywgaW50ZXJhY3RpdmUgPSBULCBjYXBwZWQgPSBUKQojIAojIGdvc3QucmVzLm5lZyA8LSBnb3N0KGxpc3QoVGFyZ2V0X0Rvd25yZWd1bGF0ZWRfR2VuZXMgPSBlbnJpY2hlZC5zZXQubmVnJGdlbmVJRCksIG9yZ2FuaXNtID0gInN0c3RlcnByamViNTI4IiwgY29ycmVjdGlvbl9tZXRob2QgPSAiZmRyIikKIyBnb3N0cGxvdChnb3N0LnJlcy5uZWcsIGludGVyYWN0aXZlID0gVCwgY2FwcGVkID0gVCkKCiMgUGVyZm9ybSBHU0VBIHVzaW5nIGNsdXN0ZXJQcm9maWxlciAtLS0tCiMgV2hpY2ggbGlicmFyeSB0byB1c2UgZm9yIGltcGxlbWVudGF0aW9uPyBBcyBwZXIgaHR0cHM6Ly9hY2FkZW1pYy5vdXAuY29tL2JpYi9hZHZhbmNlLWFydGljbGUvZG9pLzEwLjEwOTMvYmliL2JiejE1OC81NzIyMzg0OiAiRm9yIGV4cHJlc3Npb24tYmFzZWQgRUEgb24gdGhlIGZ1bGwgZXhwcmVzc2lvbiBtYXRyaXguLi5XaGVuIGdpdmVuIHJhdyByZWFkIGNvdW50cywgd2UgcmVjb21tZW5kIHRvIGFwcGx5IGEgVlNUIHN1Y2ggYXMgdm9vbSBbMzldIHRvIGFycml2ZSBhdCBsaWJyYXJ5LXNpemUgbm9ybWFsaXplZCBsb2dDUE1zLiIKIyBGb3IgdGVzdGluZyBzZWxmLWNvbnRhaW5lZCBudWxsIGh5cG90aGVzaXMgKHRlc3QgZm9yIGFzc29jaWF0aW9uIG9mIGFueSBnZW5lIGluIHRoZSBzZXQgd2l0aCB0aGUgcGhlbm90eXBlKSwgdXNlIFJPQVNUCiMgRm9yIHRlc3RpbmcgY29tcGV0aXRpdmUgbnVsbCBoeXBvdGhlc2lzICh0ZXN0IGZvciBleGNlc3Mgb2YgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gaW4gYSBnZW5lIHNldCByZWxhdGl2ZSB0byBnZW5lcyBvdXRzaWRlIHRoZSBzZXQpIC0gKip0aGVpciByZWNvbW1lbmRhdGlvbioqLCB1c2UgUEFET0cgb3IgU0FGRT8KIyAKIyBBYmlsaXR5IHRvIGRvIHRoaXMgZGVwZW5kcyBvbiB0aGUgYXZhaWxhYmlsaXR5IG9mIGdlbmUgc2V0cy4gTWFqb3IgZGF0YWJhc2VzIChlLmcuIG1zaWdkYiBkb24ndCBzZWVtIHRvIGhhdmUgc3RlcmNvcmFsaXMgaW5mb3JtYXRpb24uIFRoZXkgZG8gaGF2ZSBDLiBlbGVnYW5zIGdlbmUgc2V0cywgYnV0IEknbSBub3QgY29udmluY2VkIHRoZSBob21vbG9neSBpbmZvcm1hdGlvbiBpcyBnb29kIGVub3VnaCBmb3IgdGhlIGNvbXBhcmlzb24gdG8gYmUgdW5iaWFzZWQvbWVhbmluZ2Z1bC4gCiMgCiMgQWx0aG91Z2ggdGhlIExvayBsYWIgZGlkIEdTRUEgaW4gUmFtYW5hdGhhbiAqZXQgYWwqIDIwMTEgKFBNSUQ6IDIxNTcyNTI0KSwgdXNpbmcgQy4gZWxlZ2FucyBob21vbG9ncyBhcmUgdHdvIG1hbnVhbGx5IGNvbXBpbGVkIGdlbmUgc2V0czogCiMgMSkgMzEgZ2VuZXMgd2l0aCBwcm9kdWN0cyBrbm93biB0byBiZSBpbW11bm9yZWFjdGl2ZSBpbiAqUy4gc3RlcmNvcmFsaXMqLWluZmVjdGVkIGh1bWFucwojIDIpIDQyIHB1dGF0aXZlbHkgaWRlbnRpZmllZCBoZWF0IHNob2NrIHByb3RlaW5zCiMgT2YgY291cnNlLCB0aGlzIGlzIGJlZm9yZSB0aGUgZ2Vub21lIHdhcyBzZXF1ZW5jZWQsIHNvIHRoZXNlIGdlbmVzIGRvbid0IGhhdmUgYXNzb2NpYXRlZCBTU1RQIG51bWJlcnMuIAojIAojIEluIEh1bnQgZXQgYWwgMjAxNiwgdGhlcmUgaXMgYW4gRW5zZW1ibCBDb21wYXJhIHByb3RlaW4gZmFtaWx5IHNldAojIE5vdGUgdGhhdCB0aGlzIHVzZXMgc3BlY2lmaWMgdHJhbnNjcmlwdCBpbmZvcm1hdGlvbiwgd2hpY2ggSSB0aHJvdyBvdXQuIAojIChlLmcuIFNTVFBfMDAwMTEzNzQwMC4yIGlzIHJlY29kZWQgYXMgU1NUUF8wMDAxMTM3NDAwKQplbnNDb21wLmdlbmVJRHMgPC0gcmVhZC54bHN4ICgiLi4vRGF0YS9IdW50X1BhcmFzaXRlX0Vuc2VtYmxfQ29tcGFyYS54bHN4IiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNoZWV0ID0gMSkgJT4lCiAgYXNfdGliYmxlKCkgJT4lCiAgZHBseXI6OnNlbGVjdCgtRmFtaWx5Lm1lbWJlcnMpICU+JQogIHBpdm90X2xvbmdlcihjb2xzID0gLUNvbXBhcmEuZmFtaWx5LmlkLCB2YWx1ZXNfdG8gPSAiZ2VuZUlEIikgJT4lCiAgZHBseXI6OnNlbGVjdCgtbmFtZSkgJT4lCiAgZHBseXI6OmZpbHRlcihncmVwbCgiU1JBRV8iLCBnZW5lSUQpKQoKZW5zQ29tcC5nZW5lSURzJGdlbmVJRCA8LSBzdHJfcmVtb3ZlX2FsbChlbnNDb21wLmdlbmVJRHMkZ2VuZUlELCAiXFwuWzAtOV0kIikKZW5zQ29tcC5nZW5lSURzJGdlbmVJRCA8LSBzdHJfcmVtb3ZlX2FsbChlbnNDb21wLmdlbmVJRHMkZ2VuZUlELCAiW2Etel0kIikKCiMgQ29tcGFyZSB0aGVzZSBnZW5lcyB0byB0aGUgbGlzdCBvZiBnZW5lcyBpbiBvdXIgZmlsdGVyZWQsIG5vcm1hbGl6ZWQgbGlzdCAtLS0tCiMgCmNvbXBhcmEuZXhjbHVzaXZlIDwtIHVuaXF1ZShlbnNDb21wLmdlbmVJRHMkZ2VuZUlEKSAlPiUKICBhc190aWJibGVfY29sKGNvbHVtbl9uYW1lID0gImdlbmVJRCIpICU+JQogIGRwbHlyOjphbnRpX2pvaW4oZGlmZkdlbmVzLmRmLCBieSA9ICJnZW5lSUQiKQpucm93KGNvbXBhcmEuZXhjbHVzaXZlKQoKY29tcGFyYS5hYnNlbnQgPC0gdW5pcXVlKGVuc0NvbXAuZ2VuZUlEcyRnZW5lSUQpICU+JQogIGFzX3RpYmJsZV9jb2woY29sdW1uX25hbWUgPSAiZ2VuZUlEIikgJT4lCiAgZHBseXI6OmFudGlfam9pbihkaWZmR2VuZXMuZGYsLiwgYnkgPSAiZ2VuZUlEIikgJT4lCiAgZHBseXI6OnNlbGVjdChnZW5lSUQpCm5yb3coY29tcGFyYS5hYnNlbnQpCgojIEhvdyBtYW55IGdlbmVzIGhhdmUgYXNzb2NpYXRlZCBHTyB0ZXJtcz8gLS0tLQpHTy5wcmVzZW50IDwtIGxpc3QubXlUb3BIaXRzLmRmW1t5eV1dJEdPX3Rlcm0gJT4lCiAgZ3N1YigiTkEiLCBOQSwuKSAlPiUKICBhc190aWJibGVfY29sKGNvbHVtbl9uYW1lID0gIkdPX1Rlcm0iKSAlPiUKICB0aWJibGUoZ2VuZUlEID0gbGlzdC5teVRvcEhpdHMuZGZbW3l5XV0kZ2VuZUlELC4pICU+JQogIGRwbHlyOjpmaWx0ZXIoIWlzLm5hKEdPX1Rlcm0pKQpucm93KEdPLnByZXNlbnQpCgojIEFyZSBhbnkgb2YgdGhlc2UgZ2VuZXMgcGFydCBvZiB0aG9zZSBub3QgZm91bmQgaW4gdGhlIGNvbXBhcmEgZGF0YXNldD8gLS0tLSAKR08ucHJlc2VudC5Db21wYXJhLmFic2VudCA8LSBkcGx5cjo6c2VtaV9qb2luKEdPLnByZXNlbnQsIGNvbXBhcmEuYWJzZW50LCBieSA9ICJnZW5lSUQiKQpucm93KEdPLnByZXNlbnQuQ29tcGFyYS5hYnNlbnQpCgojIE1ha2UgYSBsaXN0IG9mIGdlbmVzCmVuc0NvbXAuZmFtaWx5SURzIDwtIHJlYWQueGxzeCAoIi4uL0RhdGEvSHVudF9QYXJhc2l0ZV9FbnNlbWJsX0NvbXBhcmEueGxzeCIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNoZWV0ID0gMiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xzID0gYygxLDQ6NikpICU+JQogIGFzX3RpYmJsZSgpICU+JQogIGRwbHlyOjptdXRhdGUoRmFtaWx5X0Rlc2NyaXB0aW9uID0gZHBseXI6OmNvYWxlc2NlKC4kRGVzY3JpcHRpb24sIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC4kYFRvcC5wcm9kdWN0LihtZW1iZXJzLndpdGguaGl0KWAsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC4kYEludGVycHJvLnRvcC5oaXQuKG1lbWJlcnMud2l0aC5oaXQpYCkKICApICU+JQogIGRwbHlyOjpzZWxlY3QoQ29tcGFyYS5mYW1pbHkuaWQsIEZhbWlseV9EZXNjcmlwdGlvbikKCmVuc0NvbXAgPC0gbGVmdF9qb2luKGVuc0NvbXAuZ2VuZUlEcywgZW5zQ29tcC5mYW1pbHlJRHMsIGJ5ID0gIkNvbXBhcmEuZmFtaWx5LmlkIikgJT4lCiAgZHBseXI6OnNlbGVjdCgtQ29tcGFyYS5mYW1pbHkuaWQpICU+JQogIGRwbHlyOjpyZW5hbWUoZ3NfbmFtZSA9IEZhbWlseV9EZXNjcmlwdGlvbiwgZ2VuZV9zeW1ib2wgPSBnZW5lSUQpICU+JQogIGRwbHlyOjpyZWxvY2F0ZShnc19uYW1lLCBnZW5lX3N5bWJvbCkKCnJtKGVuc0NvbXAuZ2VuZUlEcywgZW5zQ29tcC5mYW1pbHlJRHMpCgojIFNhdmUgZGF0YWJhc2Ugb2YgcGFyYXNpdGUgR2VuZSBTZXRzIGZvciBpbXBvcnQgaW50byBTaGlueSBBcHAuIC0tLS0KCiMgRmlsdGVyIG91dCBnZW5lcyB0aGF0IGFyZW4ndCBwYXJ0IG9mIG91ciBSTkFzZXEgZGF0YXNldApnZW5lbGlzdCA8LSB2LkRFR0xpc3QuZmlsdGVyZWQubm9ybSRnZW5lcyAlPiUKICByb3duYW1lc190b19jb2x1bW4odmFyID0gImdlbmVJRCIpICU+JQogIGRwbHlyOjpzZWxlY3QoZ2VuZUlEKQplbnNDb21wPC0gZW5zQ29tcCAlPiUKICBkcGx5cjo6cmVuYW1lKGdlbmVJRCA9IGdlbmVfc3ltYm9sKSAlPiUKICBsZWZ0X2pvaW4oZ2VuZWxpc3QsIC4sIGJ5ID0gImdlbmVJRCIpICU+JQogIGRwbHlyOjpyZWxvY2F0ZShnc19uYW1lLCBnZW5lSUQpCgpzYXZlKGVuc0NvbXAsCiAgICAgZmlsZSA9ICIuLi9PdXRwdXRzL1NyX3BhcmFzaXRlR2VuZVNldHMiKQoKIyBHZW5lcmF0ZSByYW5rIG9yZGVyZWQgbGlzdCBvZiBnZW5lcyAtLS0tCm15ZGF0YS5kZi5zdWIgPC0gZHBseXI6OnNlbGVjdChsaXN0Lm15VG9wSGl0cy5kZltbeXldXSwgZ2VuZUlELCBsb2dGQykKbXlkYXRhLmdzZWEgPC0gbXlkYXRhLmRmLnN1YiRsb2dGQwpuYW1lcyhteWRhdGEuZ3NlYSkgPC0gYXMuY2hhcmFjdGVyKG15ZGF0YS5kZi5zdWIkZ2VuZUlEKQpteWRhdGEuZ3NlYSA8LSBzb3J0KG15ZGF0YS5nc2VhLCBkZWNyZWFzaW5nID0gVFJVRSkKCiMgcnVuIEdTRUEgdXNpbmcgdGhlICdHU0VBJyBmdW5jdGlvbiBmcm9tIGNsdXN0ZXJQcm9maWxlcgojIEdpdmVuIGEgcHJpb3JpIGRlZmluZWQgc2V0IG9mIGdlbmUgUyAoZS5nLiwgZ2VuZXMgc2hhcmVpbmcgdGhlIHNhbWUgRE8gY2F0ZWdvcnkpLCB0aGUgZ29hbCBvZiBHU0VBIGlzIHRvIGRldGVybWluZSB3aGV0aGVyIHRoZSBtZW1iZXJzIG9mIFMgYXJlIHJhbmRvbWx5IGRpc3RyaWJ1dGVkIHRocm91Z2hvdXQgdGhlIHJhbmtlZCBnZW5lIGxpc3QgKEwpIG9yIHByaW1hcmlseSBmb3VuZCBhdCB0aGUgdG9wIG9yIGJvdHRvbS4KIyBUaGVyZSBhcmUgdGhyZWUga2V5IGVsZW1lbnRzIG9mIHRoZSBHU0VBIG1ldGhvZDoKIyAqKkNhbGN1bGF0aW9uIG9mIGFuIEVucmljaG1lbnQgU2NvcmUuKioKIyBUaGUgZW5yaWNobWVudCBzY29yZSAoRVMpIHJlcHJlc2VudCB0aGUgZGVncmVlIHRvIHdoaWNoIGEgc2V0IFMgaXMgb3Zlci1yZXByZXNlbnRlZCBhdCB0aGUgdG9wIG9yIGJvdHRvbSBvZiB0aGUgcmFua2VkIGxpc3QgTC4gVGhlIHNjb3JlIGlzIGNhbGN1bGF0ZWQgYnkgd2Fsa2luZyBkb3duIHRoZSBsaXN0IEwsIGluY3JlYXNpbmcgYSBydW5uaW5nLXN1bSBzdGF0aXN0aWMgd2hlbiB3ZSBlbmNvdW50ZXIgYSBnZW5lIGluIFMgYW5kIGRlY3JlYXNpbmcgd2hlbiBpdCBpcyBub3QuIFRoZSBtYWduaXR1ZGUgb2YgdGhlIGluY3JlbWVudCBkZXBlbmRzIG9uIHRoZSBnZW5lIHN0YXRpc3RpY3MgKGUuZy4sIGNvcnJlbGF0aW9uIG9mIHRoZSBnZW5lIHdpdGggcGhlbm90eXBlKS4gVGhlIEVTIGlzIHRoZSBtYXhpbXVtIGRldmlhdGlvbiBmcm9tIHplcm8gZW5jb3VudGVyZWQgaW4gdGhlIHJhbmRvbSB3YWxrOyBpdCBjb3JyZXNwb25kcyB0byBhIHdlaWdodGVkIEtvbG1vZ29yb3YtU21pcm5vdi1saWtlIHN0YXRpc3RpYyAoU3VicmFtYW5pYW4gZXQgYWwuIDIwMDUpLgojICoqRXNpbWF0aW9uIG9mIFNpZ25pZmljYW5jZSBMZXZlbCBvZiBFUy4qKgojIFRoZSBwLXZhbHVlIG9mIHRoZSBFUyBpcyBjYWxjdWxhdGVkIHVzaW5nIHBlcm11dGF0aW9uIHRlc3QuIFNwZWNpZmljYWxseSwgd2UgcGVybXV0ZSB0aGUgZ2VuZSBsYWJlbHMgb2YgdGhlIGdlbmUgbGlzdCBMIGFuZCByZWNvbXB1dGUgdGhlIEVTIG9mIHRoZSBnZW5lIHNldCBmb3IgdGhlIHBlcm11dGF0ZWQgZGF0YSwgd2hpY2ggZ2VuZXJhdGUgYSBudWxsIGRpc3RyaWJ1dGlvbiBmb3IgdGhlIEVTLiBUaGUgcC12YWx1ZSBvZiB0aGUgb2JzZXJ2ZWQgRVMgaXMgdGhlbiBjYWxjdWxhdGVkIHJlbGF0aXZlIHRvIHRoaXMgbnVsbCBkaXN0cmlidXRpb24uCiMgKipBZGp1c3RtZW50IGZvciBNdWx0aXBsZSBIeXBvdGhlc2lzIFRlc3RpbmcuKioKIyBXaGVuIHRoZSBlbnRpcmUgZ2VuZSBzZXRzIHdlcmUgZXZhbHVhdGVkLCBET1NFIGFkanVzdCB0aGUgZXN0aW1hdGVkIHNpZ25pZmljYW5jZSBsZXZlbCB0byBhY2NvdW50IGZvciBtdWx0aXBsZSBoeXBvdGhlc2lzIHRlc3RpbmcgYW5kIGFsc28gcS12YWx1ZXMgd2VyZSBjYWxjdWxhdGVkIGZvciBGRFIgY29udHJvbC4KbXlHU0VBLnJlcyA8LSBHU0VBKG15ZGF0YS5nc2VhLCBURVJNMkdFTkU9ZW5zQ29tcCwgdmVyYm9zZT1GQUxTRSkKbXlHU0VBLmRmIDwtIGFzX3RpYmJsZShteUdTRUEucmVzQHJlc3VsdCkKCm15R1NFQS50Ymw8LWFzX3RpYmJsZShteUdTRUEucmVzQHJlc3VsdCkgJT4lCiAgZHBseXI6OnNlbGVjdCgtYyhEZXNjcmlwdGlvbiwgcHZhbHVlLCBlbnJpY2htZW50U2NvcmUpKSAlPiUKICBkcGx5cjo6cmVuYW1lKG5vcm1hbGl6ZWRfRW5yaWNobWVudFNjb3JlID0gTkVTKQoKIyB2aWV3IHJlc3VsdHMgYXMgYW4gaW50ZXJhY3RpdmUgdGFibGUKZW5yaWNobWVudC5EVCA8LSBkYXRhdGFibGUobXlHU0VBLnRibCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHJvd25hbWVzID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgY2FwdGlvbiA9ICBodG1sdG9vbHM6OnRhZ3MkY2FwdGlvbigKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdHlsZSA9ICdjYXB0aW9uLXNpZGU6IHRvcDsgdGV4dC1hbGlnbjogbGVmdDsgY29sb3I6IGJsYWNrJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBodG1sdG9vbHM6OnRhZ3MkYignR2VuZSBGYW1pbGllcyBFbnJpY2hlZCBpbiAnLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBnc3ViKCctJywnIHZzICcsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuYW1lcyhsaXN0Lm15VG9wSGl0cy5kZilbW3l5XV0pKQogICAgICAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICAgICAgICAgICAgICAgICAgICBvcHRpb25zID0gbGlzdCgKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhdXRvV2lkdGggPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNjcm9sbFggPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICNzY3JvbGxZID0gJzgwMHB4JywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzY3JvbGxDb2xsYXBzZSA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VhcmNoSGlnaGxpZ2h0ID0gVFJVRSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb3JkZXIgPSBsaXN0KDMsICdkZXNjJyksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFnZUxlbmd0aCA9IDI1LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZW5ndGhNZW51ID0gYygiNSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjEwIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiMjUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICI1MCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjEwMCIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbHVtbkRlZnMgPSBsaXN0KAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGlzdCh0YXJnZXRzID0gIl9hbGwiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjbGFzcz0iZHQtcmlnaHQiKSkpKSAlPiUKICBmb3JtYXRSb3VuZChjb2x1bW5zPWMoMyw1OjYpLCBkaWdpdHM9MikgJT4lCiAgZm9ybWF0Um91bmQoY29sdW1ucz1jKDQpLCBkaWdpdHM9NCkKZW5yaWNobWVudC5EVAoKIyBjcmVhdGUgZW5yaWNobWVudCBwbG90cyB1c2luZyB0aGUgZW5yaWNocGxvdCBwYWNrYWdlCiMgZ3NlYXBsb3QyKG15R1NFQS5yZXMsIAojICAgICAgICAgICBnZW5lU2V0SUQgPSAzLCAjY2FuIGNob29zZSBtdWx0aXBsZSBzaWduYXR1cmVzIHRvIG92ZXJsYXkgaW4gdGhpcyBwbG90CiMgICAgICAgICAgIHB2YWx1ZV90YWJsZSA9IEZBTFNFLCAjY2FuIHNldCB0aGlzIHRvIEZBTFNFIGZvciBhIGNsZWFuZXIgcGxvdAojICAgICAgICAgICB0aXRsZSA9ICJTQ1AvVEFQIEdlbmUgU2V0IikgI2NhbiBhbHNvIHR1cm4gb2ZmIHRoaXMgdGl0bGUKCiMgYWRkIGEgdmFyaWFibGUgdG8gdGhpcyByZXN1bHQgdGhhdCBtYXRjaGVzIGVucmljaG1lbnQgZGlyZWN0aW9uIHdpdGggcGhlbm90eXBlCm15R1NFQS5kZiA8LSBteUdTRUEuZGYgJT4lCiAgbXV0YXRlKGxpZmVfc3RhZ2UgPSBjYXNlX3doZW4oCiAgICBORVMgPiAwIH4gc3RyX3NwbGl0KG5hbWVzKGxpc3QubXlUb3BIaXRzLmRmKVtbeXldXSwnLScsc2ltcGxpZnkgPSBUKVsxLDFdLAogICAgTkVTIDwgMCB+IHN0cl9zcGxpdChuYW1lcyhsaXN0Lm15VG9wSGl0cy5kZilbW3l5XV0sJy0nLHNpbXBsaWZ5ID0gVClbMSwyXSkpCgpteUdTRUEuZGYkSUQgPC0gbXlHU0VBLmRmJElEICU+JQogIHdvcmQoc2VwID0gJywnKSAlPiUKICAjd29yZChzZXAgPSAnLycpICU+JQogIHdvcmQoc2VwID0gJyBhbmQnKQoKIyBjcmVhdGUgJ2J1YmJsZSBwbG90JyB0byBzdW1tYXJpemUgeSBzaWduYXR1cmVzIGFjcm9zcyB4IHBoZW5vdHlwZXMKZ2dwbG90KG15R1NFQS5kZiwgYWVzKHg9bGlmZV9zdGFnZSwgeT1JRCkpICsgCiAgZ2VvbV9wb2ludChhZXMoc2l6ZT1zZXRTaXplLCBjb2xvciA9IE5FUywgYWxwaGE9LWxvZzEwKHAuYWRqdXN0KSkpICsKICBzY2FsZV9jb2xvcl9ncmFkaWVudChsb3c9ImJsdWUiLCBoaWdoPSJyZWQiKSArCiAgbGFicyh0aXRsZSA9IHBhc3RlMCgnR2VuZSBGYW1pbGllcyBFbnJpY2hlZCBpbiAnLCAKICAgICAgICAgICAgICAgICAgICAgIGdzdWIoJy0nLCcgdnMgJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgbmFtZXMobGlzdC5teVRvcEhpdHMuZGYpW1t5eV1dKSksCiAgICAgICBzdWJ0aXRsZSA9ICdORVMgPSBOb3JtYWxpemVkIEVucmljaG1lbnQgU2NvcmU7IEdlbmUgZmFtaWx5IGFzc2lnbm1lbnRzIAogICAgICAgICAgICAgZnJvbSBFbnNlbWJsIENvbXBhcmEgZGF0YXNldCBkZWZpbmVkIGluIEh1bnQgZXQgYWwgMjAxNicsCiAgICAgICB4ID0gIkxpZmUgU3RhZ2UiLAogICAgICAgeSA9ICJGYW1pbHkgSUQiKSArCiAgI2Nvb3JkX2ZpeGVkKDEvMikgKwogIHRoZW1lX2J3KCkgKwogIHRoZW1lKHBsb3QudGl0bGUucG9zaXRpb24gPSAicGxvdCIsCiAgICAgICAgcGxvdC5jYXB0aW9uLnBvc2l0aW9uID0gInBsb3QiLAogICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoZmFjZSA9ICJib2xkIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpemUgPSAxMywgaGp1c3QgPSAwKSwKICAgICAgICBheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KGZhY2UgPSAiYm9sZCIsc2l6ZSA9IDEwLjQpLAogICAgICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dChmYWNlPSJib2xkIixzaXplID0gMTAuNCksCiAgICAgICAgYXNwZWN0LnJhdGlvID0gMy8xKQoKYGBgCgoKCg==